gate-protect-widget 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +323 -0
- package/dist/paywall-widget.esm.js +34 -0
- package/dist/paywall-widget.min.js +34 -0
- package/package.json +41 -0
- package/types/index.d.ts +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# 🛡️ PaywallProtect Widget
|
|
2
|
+
|
|
3
|
+
JavaScript widget for protecting content from AI bots while allowing human visitors.
|
|
4
|
+
|
|
5
|
+
**Configured for:** acvetne@gmail.com
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
### 1. Generate Your Credentials
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
node setup.js
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This will create:
|
|
16
|
+
- A unique Site ID
|
|
17
|
+
- A unique API Key
|
|
18
|
+
- A configuration file (`widget-config.html`)
|
|
19
|
+
|
|
20
|
+
### 2. Add Widget to Your Site
|
|
21
|
+
|
|
22
|
+
Copy this code and paste it before the closing `</body>` tag on your website:
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<script
|
|
26
|
+
src="dist/paywall-widget.min.js"
|
|
27
|
+
data-site-id="YOUR_SITE_ID"
|
|
28
|
+
data-api-key="YOUR_API_KEY"
|
|
29
|
+
data-api-url="YOUR_SUPABASE_URL/functions/v1"
|
|
30
|
+
data-debug="true"
|
|
31
|
+
async
|
|
32
|
+
></script>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Get your credentials from:**
|
|
36
|
+
- Run `node setup.js` to generate them
|
|
37
|
+
- OR create a site in the [Dashboard](http://localhost:5173/dashboard)
|
|
38
|
+
|
|
39
|
+
### 3. That's It!
|
|
40
|
+
|
|
41
|
+
The widget will automatically:
|
|
42
|
+
- ✅ **Allow all humans** - No interruption for real visitors
|
|
43
|
+
- ❌ **Block AI bots** - GPTBot, ClaudeBot, CCBot, etc.
|
|
44
|
+
- ✅ **Allow SEO bots** - Google, Bing, etc. for search indexing
|
|
45
|
+
- 📊 **Log everything** - View traffic in your dashboard
|
|
46
|
+
|
|
47
|
+
## Default Behavior
|
|
48
|
+
|
|
49
|
+
**The widget is pre-configured to ONLY block bots:**
|
|
50
|
+
|
|
51
|
+
| Visitor Type | Behavior |
|
|
52
|
+
|--------------|----------|
|
|
53
|
+
| 👤 Humans | ✅ Always allowed - no paywall |
|
|
54
|
+
| 🤖 AI Bots (GPTBot, ClaudeBot) | ❌ Blocked |
|
|
55
|
+
| 🔍 SEO Bots (Google, Bing) | ✅ Allowed |
|
|
56
|
+
| 🕷️ Scrapers | ❌ Blocked |
|
|
57
|
+
|
|
58
|
+
**You will NEVER see a paywall as a human visitor** - this is the default configuration.
|
|
59
|
+
|
|
60
|
+
## Configuration Options
|
|
61
|
+
|
|
62
|
+
Customize widget behavior with data attributes:
|
|
63
|
+
|
|
64
|
+
```html
|
|
65
|
+
<script
|
|
66
|
+
src="dist/paywall-widget.min.js"
|
|
67
|
+
data-site-id="site_xxx"
|
|
68
|
+
data-api-key="pk_live_xxx"
|
|
69
|
+
data-api-url="https://your-project.supabase.co/functions/v1"
|
|
70
|
+
|
|
71
|
+
<!-- Optional Configuration -->
|
|
72
|
+
data-debug="true" <!-- Enable debug logging -->
|
|
73
|
+
data-mode="auto" <!-- auto | always | never -->
|
|
74
|
+
data-seo-safe="true" <!-- Allow search engine bots -->
|
|
75
|
+
data-protect-body="true" <!-- Protect entire page -->
|
|
76
|
+
data-subscribe-url="/subscribe" <!-- Custom subscribe URL -->
|
|
77
|
+
data-login-url="/login" <!-- Custom login URL -->
|
|
78
|
+
async
|
|
79
|
+
></script>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Available Modes
|
|
83
|
+
|
|
84
|
+
- **`auto`** (default) - Automatically detect bots and block them
|
|
85
|
+
- **`always`** - Always show paywall (for testing)
|
|
86
|
+
- **`never`** - Disable widget entirely
|
|
87
|
+
|
|
88
|
+
## Dashboard Setup
|
|
89
|
+
|
|
90
|
+
### Option 1: Using the Dashboard (Recommended)
|
|
91
|
+
|
|
92
|
+
1. Go to [http://localhost:5173/dashboard](http://localhost:5173/dashboard)
|
|
93
|
+
2. Sign in with `acvetne@gmail.com`
|
|
94
|
+
3. Click **"Add New Site"**
|
|
95
|
+
4. Enter your site details:
|
|
96
|
+
- Site Name: "My Website"
|
|
97
|
+
- Domain: "example.com"
|
|
98
|
+
5. **Copy the Site ID and API Key** shown
|
|
99
|
+
6. Add the widget code to your site
|
|
100
|
+
|
|
101
|
+
### Option 2: Automatic Setup Script
|
|
102
|
+
|
|
103
|
+
Run the setup script to generate credentials automatically:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
node setup.js
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
This creates a `widget-config.html` file with your unique credentials and integration instructions.
|
|
110
|
+
|
|
111
|
+
## Testing the Widget
|
|
112
|
+
|
|
113
|
+
### View Demo Page
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
open demo.html
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The demo page shows:
|
|
120
|
+
- How the widget integrates
|
|
121
|
+
- Debug controls
|
|
122
|
+
- Configuration examples
|
|
123
|
+
|
|
124
|
+
### Debug in Browser Console
|
|
125
|
+
|
|
126
|
+
Once the widget is loaded, you can use these commands in the browser console:
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
// View current status
|
|
130
|
+
PaywallProtect.debug.status()
|
|
131
|
+
|
|
132
|
+
// View all logs
|
|
133
|
+
PaywallProtect.logs.getAll()
|
|
134
|
+
|
|
135
|
+
// Download logs as JSON
|
|
136
|
+
PaywallProtect.logs.download()
|
|
137
|
+
|
|
138
|
+
// Export logs to console
|
|
139
|
+
console.log(PaywallProtect.logs.export())
|
|
140
|
+
|
|
141
|
+
// Test paywall display
|
|
142
|
+
PaywallProtect.showPaywall({ type: 'hard' })
|
|
143
|
+
|
|
144
|
+
// Hide paywall
|
|
145
|
+
PaywallProtect.hidePaywall()
|
|
146
|
+
|
|
147
|
+
// Force reload/recheck
|
|
148
|
+
PaywallProtect.reload()
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Testing with Different User Agents
|
|
152
|
+
|
|
153
|
+
Test bot detection by changing your user agent:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# Using curl (will be detected as bot)
|
|
157
|
+
curl http://localhost:5173
|
|
158
|
+
|
|
159
|
+
# Using a bot user agent
|
|
160
|
+
curl -A "GPTBot/1.0" http://localhost:5173
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Building the Widget
|
|
164
|
+
|
|
165
|
+
### Development
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
# Install dependencies
|
|
169
|
+
npm install
|
|
170
|
+
|
|
171
|
+
# Build the widget
|
|
172
|
+
npm run build
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
This creates:
|
|
176
|
+
- `dist/paywall-widget.min.js` - Minified IIFE version
|
|
177
|
+
- `dist/paywall-widget.esm.js` - ESM module version
|
|
178
|
+
|
|
179
|
+
### Distribution
|
|
180
|
+
|
|
181
|
+
The built files can be:
|
|
182
|
+
- Hosted on your CDN
|
|
183
|
+
- Included directly in your site
|
|
184
|
+
- Distributed via npm
|
|
185
|
+
|
|
186
|
+
## How It Works
|
|
187
|
+
|
|
188
|
+
1. **Widget loads** on your page
|
|
189
|
+
2. **Fingerprints browser** - Collects canvas, WebGL, plugins, timing data
|
|
190
|
+
3. **Checks user agent** - Identifies known bots
|
|
191
|
+
4. **Calls API** - Sends fingerprint to your backend
|
|
192
|
+
5. **Backend decides** - Bot detection + paywall logic
|
|
193
|
+
6. **Widget responds**:
|
|
194
|
+
- **If bot:** Silently blocked
|
|
195
|
+
- **If human:** Full access (by default)
|
|
196
|
+
- **If paywall enabled for humans:** Shows paywall UI
|
|
197
|
+
|
|
198
|
+
## Allowed Bots (SEO-Safe)
|
|
199
|
+
|
|
200
|
+
These bots are allowed by default for SEO:
|
|
201
|
+
|
|
202
|
+
- Googlebot (Google Search)
|
|
203
|
+
- Bingbot (Bing Search)
|
|
204
|
+
- DuckDuckBot (DuckDuckGo)
|
|
205
|
+
- Baiduspider (Baidu)
|
|
206
|
+
- Yandexbot (Yandex)
|
|
207
|
+
- FacebookExternalHit (Facebook previews)
|
|
208
|
+
- TwitterBot (Twitter cards)
|
|
209
|
+
- LinkedInBot (LinkedIn previews)
|
|
210
|
+
- SlackBot (Slack previews)
|
|
211
|
+
|
|
212
|
+
## Blocked Bots
|
|
213
|
+
|
|
214
|
+
These bots are automatically blocked:
|
|
215
|
+
|
|
216
|
+
- **GPTBot** (OpenAI)
|
|
217
|
+
- **ClaudeBot** (Anthropic)
|
|
218
|
+
- **CCBot** (Common Crawl)
|
|
219
|
+
- **Cohere-AI** (Cohere)
|
|
220
|
+
- Generic scrapers (curl, wget, python-requests, scrapy)
|
|
221
|
+
|
|
222
|
+
## API Reference
|
|
223
|
+
|
|
224
|
+
### JavaScript API
|
|
225
|
+
|
|
226
|
+
```javascript
|
|
227
|
+
// Global PaywallProtect object
|
|
228
|
+
window.PaywallProtect = {
|
|
229
|
+
version: '1.2.1',
|
|
230
|
+
|
|
231
|
+
// Manual controls
|
|
232
|
+
showPaywall: (config) => {},
|
|
233
|
+
hidePaywall: () => {},
|
|
234
|
+
checkAccess: () => Promise,
|
|
235
|
+
reload: () => {},
|
|
236
|
+
|
|
237
|
+
// Configuration
|
|
238
|
+
config: { ... },
|
|
239
|
+
allowedBots: [ ... ],
|
|
240
|
+
|
|
241
|
+
// Logging
|
|
242
|
+
logs: {
|
|
243
|
+
getAll: () => Array,
|
|
244
|
+
export: () => String,
|
|
245
|
+
clear: () => void,
|
|
246
|
+
download: () => void
|
|
247
|
+
},
|
|
248
|
+
|
|
249
|
+
// Debug helpers
|
|
250
|
+
debug: {
|
|
251
|
+
enable: () => void,
|
|
252
|
+
disable: () => void,
|
|
253
|
+
status: () => void
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Troubleshooting
|
|
259
|
+
|
|
260
|
+
### Widget not loading?
|
|
261
|
+
|
|
262
|
+
Check browser console for errors:
|
|
263
|
+
|
|
264
|
+
```javascript
|
|
265
|
+
// Expected output:
|
|
266
|
+
[PaywallProtect] Initializing PaywallProtect Widget
|
|
267
|
+
[PaywallProtect] ✓ Configuration validated
|
|
268
|
+
[PaywallProtect] ✓ Access granted, no protection applied
|
|
269
|
+
[PaywallProtect] ✓ PaywallProtect initialization complete
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Seeing "Missing required configuration"?
|
|
273
|
+
|
|
274
|
+
Make sure you have:
|
|
275
|
+
- `data-site-id="..."`
|
|
276
|
+
- `data-api-key="..."`
|
|
277
|
+
- `data-api-url="..."`
|
|
278
|
+
|
|
279
|
+
All three are required.
|
|
280
|
+
|
|
281
|
+
### Getting "payment required" as a human?
|
|
282
|
+
|
|
283
|
+
This means your site configuration has `showPaywallToHumans: true`.
|
|
284
|
+
|
|
285
|
+
**To fix:**
|
|
286
|
+
1. Go to your [Dashboard](http://localhost:5173/dashboard)
|
|
287
|
+
2. Find your site and click "Configure"
|
|
288
|
+
3. Ensure "Show Paywall to Humans" is **disabled**
|
|
289
|
+
|
|
290
|
+
The default is disabled - only bots are blocked.
|
|
291
|
+
|
|
292
|
+
### Widget not blocking bots?
|
|
293
|
+
|
|
294
|
+
Check that your API endpoint is working:
|
|
295
|
+
|
|
296
|
+
```javascript
|
|
297
|
+
// In browser console
|
|
298
|
+
fetch('YOUR_SUPABASE_URL/functions/v1/check-access', {
|
|
299
|
+
method: 'POST',
|
|
300
|
+
headers: { 'Content-Type': 'application/json' },
|
|
301
|
+
body: JSON.stringify({
|
|
302
|
+
siteId: 'YOUR_SITE_ID',
|
|
303
|
+
apiKey: 'YOUR_API_KEY',
|
|
304
|
+
page: '/',
|
|
305
|
+
userAgent: navigator.userAgent,
|
|
306
|
+
fingerprint: {}
|
|
307
|
+
})
|
|
308
|
+
}).then(r => r.json()).then(console.log)
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Support
|
|
312
|
+
|
|
313
|
+
- **Dashboard:** [http://localhost:5173/dashboard](http://localhost:5173/dashboard)
|
|
314
|
+
- **Demo:** [http://localhost:5173/demo](http://localhost:5173/demo)
|
|
315
|
+
- **Email:** acvetne@gmail.com
|
|
316
|
+
|
|
317
|
+
## License
|
|
318
|
+
|
|
319
|
+
MIT License - See LICENSE file for details
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
**Made with ❤️ for protecting content from AI bots**
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
var k=(s,y,a)=>new Promise((t,i)=>{var l=d=>{try{g(a.next(d))}catch(m){i(m)}},h=d=>{try{g(a.throw(d))}catch(m){i(m)}},g=d=>d.done?t(d.value):Promise.resolve(d.value).then(l,h);g((a=a.apply(s,y)).next())});(function(){"use strict";let s=document.currentScript||document.querySelector("script[data-site-id]");if(!s){console.error("[PaywallProtect] Script tag not found");return}let y=["googlebot","bingbot","slurp","duckduckbot","baiduspider","yandexbot","facebookexternalhit","twitterbot","linkedinbot","slackbot","telegrambot","whatsapp","discordbot","pinterestbot","redditbot","applebot"],a={siteId:s.dataset.siteId||s.getAttribute("data-site-id"),apiKey:s.dataset.apiKey||s.getAttribute("data-api-key"),apiUrl:s.dataset.apiUrl||s.getAttribute("data-api-url")||"https://bakzzkadgmyvvvnpuvki.supabase.co/functions/v1",subscribeUrl:s.dataset.subscribeUrl||"/subscribe",loginUrl:s.dataset.loginUrl||"/login",mode:s.dataset.mode||"auto",seoSafe:s.dataset.seoSafe!=="false",allowBots:s.dataset.allowBots||null,protectBody:s.dataset.protectBody!=="false",debug:s.dataset.debug==="true"||s.dataset.debug==="1"},t={sessionId:"session_"+Date.now()+"_"+Math.random().toString(36).substr(2,9),logs:[],startTime:performance.now(),log(e,o,r={}){let n=new Date().toISOString(),u=Math.round(performance.now()-this.startTime),f={timestamp:n,elapsed:u,level:e,message:o,data:r,sessionId:this.sessionId};this.logs.push(f);let p="[PaywallProtect]",c=a.debug?` [+${u}ms]`:"";switch(e){case"info":console.log(`%c${p}${c} ${o}`,"color: #3b82f6",r);break;case"success":console.log(`%c${p}${c} \u2713 ${o}`,"color: #10b981; font-weight: bold",r);break;case"warn":console.warn(`${p}${c} \u26A0 ${o}`,r);break;case"error":console.error(`${p}${c} \u2717 ${o}`,r);break;case"debug":a.debug&&console.log(`%c${p}${c} [DEBUG] ${o}`,"color: #8b5cf6",r);break}this.logs.length>100&&this.logs.shift()},info(e,o){this.log("info",e,o)},success(e,o){this.log("success",e,o)},warn(e,o){this.log("warn",e,o)},error(e,o){this.log("error",e,o)},debug(e,o){this.log("debug",e,o)},getAll(){return[...this.logs]},export(){return JSON.stringify({sessionId:this.sessionId,config:{siteId:a.siteId,mode:a.mode,seoSafe:a.seoSafe,protectBody:a.protectBody,debug:a.debug},userAgent:navigator.userAgent,page:window.location.href,logs:this.logs,performance:{totalTime:Math.round(performance.now()-this.startTime),memory:performance.memory?{used:Math.round(performance.memory.usedJSHeapSize/1024/1024)+"MB",total:Math.round(performance.memory.totalJSHeapSize/1024/1024)+"MB"}:"N/A"}},null,2)},clear(){this.logs=[],console.clear(),this.info("Logs cleared")}},i=[];if(a.seoSafe&&(i=[...y]),a.allowBots){let e=a.allowBots.split(",").map(o=>o.trim().toLowerCase());i=[...i,...e],t.info("Custom allowlist added",{bots:e})}if(t.info("Initializing PaywallProtect Widget",{version:"1.2.1",siteId:a.siteId,mode:a.mode,seoSafe:a.seoSafe,protectBody:a.protectBody,debug:a.debug,allowedBotsCount:i.length}),!a.siteId||!a.apiKey){t.error("Missing required configuration",{hasSiteId:!!a.siteId,hasApiKey:!!a.apiKey});return}if(!a.apiUrl){t.error("Missing API URL",{apiUrl:a.apiUrl});return}t.success("Configuration validated"),a.seoSafe&&t.info("SEO-safe mode enabled",{allowedBots:i.length});let l={marks:{},mark(e){this.marks[e]=performance.now(),t.debug(`Performance mark: ${e}`,{time:Math.round(this.marks[e])+"ms"})},measure(e,o){let r=Math.round(performance.now()-this.marks[o]);return t.debug(`Performance: ${e}`,{duration:r+"ms"}),r}};l.mark("init_start");function h(){l.mark("allowlist_check_start");let e=navigator.userAgent.toLowerCase();t.debug("Checking user agent against allowlist",{userAgent:navigator.userAgent,allowlistSize:i.length});for(let o of i)if(e.includes(o))return l.measure("Allowlist check complete","allowlist_check_start"),t.success("Allowed bot detected",{bot:o,userAgent:navigator.userAgent}),!0;return l.measure("Allowlist check complete","allowlist_check_start"),t.debug("Not in allowlist, will check with server"),!1}function g(){l.mark("fingerprint_start"),t.debug("Generating browser fingerprint");let e={userAgent:navigator.userAgent,language:navigator.language,platform:navigator.platform,hardwareConcurrency:navigator.hardwareConcurrency||0,deviceMemory:navigator.deviceMemory||0,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,screen:{width:window.screen.width,height:window.screen.height,colorDepth:window.screen.colorDepth,pixelRatio:window.devicePixelRatio||1},timing:{pageLoadTime:Math.round(performance.now())},webdriver:navigator.webdriver||!1,canvas:d(),webgl:m(),touchSupport:"ontouchstart"in window,plugins:x()};return l.measure("Fingerprint generated","fingerprint_start"),t.debug("Fingerprint complete",{hasCanvas:!!e.canvas,hasWebGL:!!e.webgl,pluginCount:e.plugins.length,webdriver:e.webdriver}),e}function d(){try{let e=document.createElement("canvas"),o=e.getContext("2d");if(!o)return t.warn("Canvas context unavailable"),null;o.textBaseline="top",o.font="14px Arial",o.fillText("PaywallProtect",2,2);let r=e.toDataURL().substring(0,100);return t.debug("Canvas fingerprint generated",{length:r.length}),r}catch(e){return t.error("Canvas fingerprint failed",{error:e.message}),null}}function m(){try{let o=document.createElement("canvas").getContext("webgl");if(!o)return t.warn("WebGL context unavailable"),null;let r={renderer:o.getParameter(o.RENDERER),vendor:o.getParameter(o.VENDOR)};return t.debug("WebGL fingerprint generated",r),r}catch(e){return t.error("WebGL fingerprint failed",{error:e.message}),null}}function x(){try{let e=Array.from(navigator.plugins||[]).map(o=>o.name).slice(0,5);return t.debug("Plugins enumerated",{count:e.length}),e}catch(e){return t.error("Plugin enumeration failed",{error:e.message}),[]}}function P(){return k(this,null,function*(){l.mark("api_call_start"),t.info("Calling API for access check",{endpoint:a.apiUrl+"/check-access"});try{let e={siteId:a.siteId,apiKey:a.apiKey,page:window.location.pathname,userAgent:navigator.userAgent,fingerprint:g(),referrer:document.referrer,allowedBots:i};t.debug("API request payload",{siteId:e.siteId,page:e.page,hasFingerprint:!!e.fingerprint,allowedBotsCount:e.allowedBots.length});let o=yield fetch(a.apiUrl+"/check-access",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}),r=l.measure("API call complete","api_call_start");if(!o.ok)return t.error("API returned error status",{status:o.status,statusText:o.statusText,duration:r+"ms"}),{allowed:!0,reason:"API error",showPaywall:!1};let n=yield o.json();return t.success("API response received",{allowed:n.allowed,status:n.status,showPaywall:n.showPaywall,reason:n.reason,duration:r+"ms"}),n}catch(e){let o=l.measure("API call failed","api_call_start");return t.error("Network error during API call",{error:e.message,stack:e.stack,duration:o+"ms"}),{allowed:!0,reason:"Network error",showPaywall:!1}}})}function w(e){if(document.getElementById("paywall-protect-modal")){t.warn("Paywall modal already exists, skipping creation");return}l.mark("modal_create_start"),t.info("Creating paywall modal",{type:e.type||"hard"});let o=e.type||"hard",r="";o==="hard"?r=`
|
|
2
|
+
<div class="paywall-icon">\u{1F512}</div>
|
|
3
|
+
<h2 id="paywall-title">${e.title||"Premium Content"}</h2>
|
|
4
|
+
<p>${e.message||"Subscribe to access this content."}</p>
|
|
5
|
+
<button id="paywall-subscribe" class="paywall-btn primary">Subscribe Now</button>
|
|
6
|
+
<p class="footer">Already a subscriber? <a href="${a.loginUrl}" id="paywall-login">Sign in</a></p>
|
|
7
|
+
`:o==="metered"?r=`
|
|
8
|
+
<div class="paywall-icon">\u{1F4CA}</div>
|
|
9
|
+
<h2 id="paywall-title">Free Article Limit Reached</h2>
|
|
10
|
+
<p>You've read <strong>${e.articlesRead||3} of ${e.freeLimit||3}</strong> free articles this month.</p>
|
|
11
|
+
<button id="paywall-subscribe" class="paywall-btn primary">Get Unlimited Access</button>
|
|
12
|
+
<button id="paywall-close" class="paywall-btn secondary">Maybe Later</button>
|
|
13
|
+
`:r=`
|
|
14
|
+
<div class="paywall-icon">\u{1F916}</div>
|
|
15
|
+
<h2 id="paywall-title">Access Denied</h2>
|
|
16
|
+
<p>${e.message||"Automated access is not permitted."}</p>
|
|
17
|
+
`;let n=document.createElement("div");if(n.id="paywall-protect-modal",n.innerHTML=`<div class="paywall-overlay"><div class="paywall-content">${r}</div></div>`,!document.getElementById("paywall-styles")){let c=document.createElement("style");c.id="paywall-styles",c.textContent=`
|
|
18
|
+
#paywall-protect-modal{position:fixed;top:0;left:0;width:100%;height:100%;z-index:999999;animation:fadeIn .3s}
|
|
19
|
+
.paywall-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.85);backdrop-filter:blur(8px);display:flex;align-items:center;justify-content:center;padding:20px}
|
|
20
|
+
.paywall-content{background:#fff;border-radius:16px;padding:48px 40px;max-width:500px;width:100%;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,.3);animation:slideUp .3s}
|
|
21
|
+
.paywall-icon{font-size:64px;margin-bottom:24px}
|
|
22
|
+
#paywall-title{font-size:28px;font-weight:700;color:#1a1a1a;margin:0 0 16px 0}
|
|
23
|
+
.paywall-content p{font-size:16px;color:#666;line-height:1.6;margin:0 0 24px 0}
|
|
24
|
+
.paywall-btn{display:block;width:100%;padding:16px 32px;border:none;border-radius:8px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s;margin-bottom:12px}
|
|
25
|
+
.paywall-btn.primary{background:#3b82f6;color:#fff}
|
|
26
|
+
.paywall-btn.primary:hover{background:#2563eb;transform:translateY(-1px)}
|
|
27
|
+
.paywall-btn.secondary{background:transparent;color:#666;border:2px solid #e5e7eb}
|
|
28
|
+
.paywall-btn.secondary:hover{background:#f9fafb}
|
|
29
|
+
.footer{margin-top:24px;font-size:14px;color:#666}
|
|
30
|
+
.footer a{color:#3b82f6;text-decoration:none}
|
|
31
|
+
.footer a:hover{text-decoration:underline}
|
|
32
|
+
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
|
|
33
|
+
@keyframes slideUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}
|
|
34
|
+
`,document.head.appendChild(c),t.debug("Paywall styles injected")}document.body.appendChild(n);let u=n.querySelector("#paywall-subscribe");u&&(u.onclick=()=>{t.info("Subscribe button clicked",{url:e.subscribeUrl||a.subscribeUrl}),window.location.href=e.subscribeUrl||a.subscribeUrl});let f=n.querySelector("#paywall-close");f&&(f.onclick=()=>{t.info("Paywall closed by user"),n.remove()});let p=n.querySelector("#paywall-login");p&&(p.onclick=c=>{c.preventDefault(),t.info("Login link clicked",{url:a.loginUrl}),window.location.href=a.loginUrl}),l.measure("Modal created","modal_create_start"),t.success("Paywall modal displayed")}function b(){l.mark("blur_start");let e=[];if(a.protectBody)e=[document.body],t.info("Protecting entire page (body element)");else{let r=["main","article",'[role="main"]',".content",".post-content",".article-content",".entry-content","#content","#main-content"];t.debug("Searching for content elements",{selectors:r});for(let n of r){let u=document.querySelector(n);if(u){e=[u],t.info("Content element found",{selector:n});break}}e.length===0&&(e=[document.body],t.warn("No content elements found, protecting body as fallback"))}let o=0;e.forEach(r=>{r.style.filter="blur(8px)",r.style.userSelect="none",r.style.pointerEvents="none",r.addEventListener("copy",n=>{n.preventDefault(),t.debug("Copy attempt blocked")}),r.addEventListener("cut",n=>{n.preventDefault(),t.debug("Cut attempt blocked")}),r.addEventListener("contextmenu",n=>{n.preventDefault(),t.debug("Context menu blocked")}),o++}),l.measure("Content blurred","blur_start"),t.success("Content protection applied",{elementsProtected:o,protectionType:a.protectBody?"body":"selective"})}function v(){return k(this,null,function*(){if(document.readyState==="loading"){t.debug("DOM not ready, waiting for DOMContentLoaded"),document.addEventListener("DOMContentLoaded",v);return}if(t.info("DOM ready, starting protection sequence"),a.mode==="never"){t.warn("Widget disabled (mode=never)");return}if(h()){t.success("Allowed bot detected, skipping all protection");return}if(a.mode==="always"){t.info("Force mode enabled (mode=always)"),b(),w({type:"hard"});return}let e=yield P();t.info("Access check decision received",{allowed:e.allowed,status:e.status,showPaywall:e.showPaywall}),!e.allowed&&e.status==="payment_required"?(t.warn("Payment required for bot access",{paymentUrl:e.paymentUrl}),e.paymentUrl?window.location.href=e.paymentUrl:(b(),w({type:"bot-blocked",message:e.reason}))):!e.allowed&&e.status==="blocked"?(t.warn("Access blocked",{reason:e.reason}),b(),w({type:"bot-blocked",message:e.reason})):e.showPaywall&&e.paywallConfig?(t.info("Showing paywall to user",{type:e.paywallConfig.type}),b(),w(e.paywallConfig)):t.success("Access granted, no protection applied");let o=l.measure("Initialization complete","init_start");t.success("PaywallProtect initialization complete",{totalTime:o+"ms"})})}v(),window.PaywallProtect={version:"1.2.1",showPaywall:e=>{t.info("Manual showPaywall() called",e),b(),w(e||{type:"hard"})},hidePaywall:()=>{t.info("Manual hidePaywall() called");let e=document.getElementById("paywall-protect-modal");e?(e.remove(),t.success("Paywall hidden")):t.warn("No paywall to hide")},checkAccess:P,reload:()=>{t.info("Manual reload() called"),v()},config:a,allowedBots:i,logs:{getAll:()=>t.getAll(),export:()=>t.export(),clear:()=>t.clear(),download:()=>{let e=t.export(),o=new Blob([e],{type:"application/json"}),r=URL.createObjectURL(o),n=document.createElement("a");n.href=r,n.download=`paywall-logs-${t.sessionId}.json`,n.click(),URL.revokeObjectURL(r),t.info("Logs downloaded")}},debug:{enable:()=>{a.debug=!0,t.success("Debug mode enabled")},disable:()=>{a.debug=!1,t.info("Debug mode disabled")},status:()=>{console.table({Version:window.PaywallProtect.version,"Session ID":t.sessionId,"Site ID":a.siteId,Mode:a.mode,"SEO Safe":a.seoSafe,"Protect Body":a.protectBody,"Debug Mode":a.debug,"Allowed Bots":i.length,"Total Logs":t.logs.length,Uptime:Math.round(performance.now()-t.startTime)+"ms"})}}},t.success("Public API initialized",{methods:Object.keys(window.PaywallProtect)})})();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
var PaywallProtect=(()=>{var k=(s,y,a)=>new Promise((t,i)=>{var l=d=>{try{g(a.next(d))}catch(m){i(m)}},h=d=>{try{g(a.throw(d))}catch(m){i(m)}},g=d=>d.done?t(d.value):Promise.resolve(d.value).then(l,h);g((a=a.apply(s,y)).next())});(function(){"use strict";let s=document.currentScript||document.querySelector("script[data-site-id]");if(!s){console.error("[PaywallProtect] Script tag not found");return}let y=["googlebot","bingbot","slurp","duckduckbot","baiduspider","yandexbot","facebookexternalhit","twitterbot","linkedinbot","slackbot","telegrambot","whatsapp","discordbot","pinterestbot","redditbot","applebot"],a={siteId:s.dataset.siteId||s.getAttribute("data-site-id"),apiKey:s.dataset.apiKey||s.getAttribute("data-api-key"),apiUrl:s.dataset.apiUrl||s.getAttribute("data-api-url")||"https://bakzzkadgmyvvvnpuvki.supabase.co/functions/v1",subscribeUrl:s.dataset.subscribeUrl||"/subscribe",loginUrl:s.dataset.loginUrl||"/login",mode:s.dataset.mode||"auto",seoSafe:s.dataset.seoSafe!=="false",allowBots:s.dataset.allowBots||null,protectBody:s.dataset.protectBody!=="false",debug:s.dataset.debug==="true"||s.dataset.debug==="1"},t={sessionId:"session_"+Date.now()+"_"+Math.random().toString(36).substr(2,9),logs:[],startTime:performance.now(),log(e,o,r={}){let n=new Date().toISOString(),u=Math.round(performance.now()-this.startTime),f={timestamp:n,elapsed:u,level:e,message:o,data:r,sessionId:this.sessionId};this.logs.push(f);let p="[PaywallProtect]",c=a.debug?` [+${u}ms]`:"";switch(e){case"info":console.log(`%c${p}${c} ${o}`,"color: #3b82f6",r);break;case"success":console.log(`%c${p}${c} \u2713 ${o}`,"color: #10b981; font-weight: bold",r);break;case"warn":console.warn(`${p}${c} \u26A0 ${o}`,r);break;case"error":console.error(`${p}${c} \u2717 ${o}`,r);break;case"debug":a.debug&&console.log(`%c${p}${c} [DEBUG] ${o}`,"color: #8b5cf6",r);break}this.logs.length>100&&this.logs.shift()},info(e,o){this.log("info",e,o)},success(e,o){this.log("success",e,o)},warn(e,o){this.log("warn",e,o)},error(e,o){this.log("error",e,o)},debug(e,o){this.log("debug",e,o)},getAll(){return[...this.logs]},export(){return JSON.stringify({sessionId:this.sessionId,config:{siteId:a.siteId,mode:a.mode,seoSafe:a.seoSafe,protectBody:a.protectBody,debug:a.debug},userAgent:navigator.userAgent,page:window.location.href,logs:this.logs,performance:{totalTime:Math.round(performance.now()-this.startTime),memory:performance.memory?{used:Math.round(performance.memory.usedJSHeapSize/1024/1024)+"MB",total:Math.round(performance.memory.totalJSHeapSize/1024/1024)+"MB"}:"N/A"}},null,2)},clear(){this.logs=[],console.clear(),this.info("Logs cleared")}},i=[];if(a.seoSafe&&(i=[...y]),a.allowBots){let e=a.allowBots.split(",").map(o=>o.trim().toLowerCase());i=[...i,...e],t.info("Custom allowlist added",{bots:e})}if(t.info("Initializing PaywallProtect Widget",{version:"1.2.1",siteId:a.siteId,mode:a.mode,seoSafe:a.seoSafe,protectBody:a.protectBody,debug:a.debug,allowedBotsCount:i.length}),!a.siteId||!a.apiKey){t.error("Missing required configuration",{hasSiteId:!!a.siteId,hasApiKey:!!a.apiKey});return}if(!a.apiUrl){t.error("Missing API URL",{apiUrl:a.apiUrl});return}t.success("Configuration validated"),a.seoSafe&&t.info("SEO-safe mode enabled",{allowedBots:i.length});let l={marks:{},mark(e){this.marks[e]=performance.now(),t.debug(`Performance mark: ${e}`,{time:Math.round(this.marks[e])+"ms"})},measure(e,o){let r=Math.round(performance.now()-this.marks[o]);return t.debug(`Performance: ${e}`,{duration:r+"ms"}),r}};l.mark("init_start");function h(){l.mark("allowlist_check_start");let e=navigator.userAgent.toLowerCase();t.debug("Checking user agent against allowlist",{userAgent:navigator.userAgent,allowlistSize:i.length});for(let o of i)if(e.includes(o))return l.measure("Allowlist check complete","allowlist_check_start"),t.success("Allowed bot detected",{bot:o,userAgent:navigator.userAgent}),!0;return l.measure("Allowlist check complete","allowlist_check_start"),t.debug("Not in allowlist, will check with server"),!1}function g(){l.mark("fingerprint_start"),t.debug("Generating browser fingerprint");let e={userAgent:navigator.userAgent,language:navigator.language,platform:navigator.platform,hardwareConcurrency:navigator.hardwareConcurrency||0,deviceMemory:navigator.deviceMemory||0,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,screen:{width:window.screen.width,height:window.screen.height,colorDepth:window.screen.colorDepth,pixelRatio:window.devicePixelRatio||1},timing:{pageLoadTime:Math.round(performance.now())},webdriver:navigator.webdriver||!1,canvas:d(),webgl:m(),touchSupport:"ontouchstart"in window,plugins:x()};return l.measure("Fingerprint generated","fingerprint_start"),t.debug("Fingerprint complete",{hasCanvas:!!e.canvas,hasWebGL:!!e.webgl,pluginCount:e.plugins.length,webdriver:e.webdriver}),e}function d(){try{let e=document.createElement("canvas"),o=e.getContext("2d");if(!o)return t.warn("Canvas context unavailable"),null;o.textBaseline="top",o.font="14px Arial",o.fillText("PaywallProtect",2,2);let r=e.toDataURL().substring(0,100);return t.debug("Canvas fingerprint generated",{length:r.length}),r}catch(e){return t.error("Canvas fingerprint failed",{error:e.message}),null}}function m(){try{let o=document.createElement("canvas").getContext("webgl");if(!o)return t.warn("WebGL context unavailable"),null;let r={renderer:o.getParameter(o.RENDERER),vendor:o.getParameter(o.VENDOR)};return t.debug("WebGL fingerprint generated",r),r}catch(e){return t.error("WebGL fingerprint failed",{error:e.message}),null}}function x(){try{let e=Array.from(navigator.plugins||[]).map(o=>o.name).slice(0,5);return t.debug("Plugins enumerated",{count:e.length}),e}catch(e){return t.error("Plugin enumeration failed",{error:e.message}),[]}}function P(){return k(this,null,function*(){l.mark("api_call_start"),t.info("Calling API for access check",{endpoint:a.apiUrl+"/check-access"});try{let e={siteId:a.siteId,apiKey:a.apiKey,page:window.location.pathname,userAgent:navigator.userAgent,fingerprint:g(),referrer:document.referrer,allowedBots:i};t.debug("API request payload",{siteId:e.siteId,page:e.page,hasFingerprint:!!e.fingerprint,allowedBotsCount:e.allowedBots.length});let o=yield fetch(a.apiUrl+"/check-access",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}),r=l.measure("API call complete","api_call_start");if(!o.ok)return t.error("API returned error status",{status:o.status,statusText:o.statusText,duration:r+"ms"}),{allowed:!0,reason:"API error",showPaywall:!1};let n=yield o.json();return t.success("API response received",{allowed:n.allowed,status:n.status,showPaywall:n.showPaywall,reason:n.reason,duration:r+"ms"}),n}catch(e){let o=l.measure("API call failed","api_call_start");return t.error("Network error during API call",{error:e.message,stack:e.stack,duration:o+"ms"}),{allowed:!0,reason:"Network error",showPaywall:!1}}})}function w(e){if(document.getElementById("paywall-protect-modal")){t.warn("Paywall modal already exists, skipping creation");return}l.mark("modal_create_start"),t.info("Creating paywall modal",{type:e.type||"hard"});let o=e.type||"hard",r="";o==="hard"?r=`
|
|
2
|
+
<div class="paywall-icon">\u{1F512}</div>
|
|
3
|
+
<h2 id="paywall-title">${e.title||"Premium Content"}</h2>
|
|
4
|
+
<p>${e.message||"Subscribe to access this content."}</p>
|
|
5
|
+
<button id="paywall-subscribe" class="paywall-btn primary">Subscribe Now</button>
|
|
6
|
+
<p class="footer">Already a subscriber? <a href="${a.loginUrl}" id="paywall-login">Sign in</a></p>
|
|
7
|
+
`:o==="metered"?r=`
|
|
8
|
+
<div class="paywall-icon">\u{1F4CA}</div>
|
|
9
|
+
<h2 id="paywall-title">Free Article Limit Reached</h2>
|
|
10
|
+
<p>You've read <strong>${e.articlesRead||3} of ${e.freeLimit||3}</strong> free articles this month.</p>
|
|
11
|
+
<button id="paywall-subscribe" class="paywall-btn primary">Get Unlimited Access</button>
|
|
12
|
+
<button id="paywall-close" class="paywall-btn secondary">Maybe Later</button>
|
|
13
|
+
`:r=`
|
|
14
|
+
<div class="paywall-icon">\u{1F916}</div>
|
|
15
|
+
<h2 id="paywall-title">Access Denied</h2>
|
|
16
|
+
<p>${e.message||"Automated access is not permitted."}</p>
|
|
17
|
+
`;let n=document.createElement("div");if(n.id="paywall-protect-modal",n.innerHTML=`<div class="paywall-overlay"><div class="paywall-content">${r}</div></div>`,!document.getElementById("paywall-styles")){let c=document.createElement("style");c.id="paywall-styles",c.textContent=`
|
|
18
|
+
#paywall-protect-modal{position:fixed;top:0;left:0;width:100%;height:100%;z-index:999999;animation:fadeIn .3s}
|
|
19
|
+
.paywall-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.85);backdrop-filter:blur(8px);display:flex;align-items:center;justify-content:center;padding:20px}
|
|
20
|
+
.paywall-content{background:#fff;border-radius:16px;padding:48px 40px;max-width:500px;width:100%;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,.3);animation:slideUp .3s}
|
|
21
|
+
.paywall-icon{font-size:64px;margin-bottom:24px}
|
|
22
|
+
#paywall-title{font-size:28px;font-weight:700;color:#1a1a1a;margin:0 0 16px 0}
|
|
23
|
+
.paywall-content p{font-size:16px;color:#666;line-height:1.6;margin:0 0 24px 0}
|
|
24
|
+
.paywall-btn{display:block;width:100%;padding:16px 32px;border:none;border-radius:8px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s;margin-bottom:12px}
|
|
25
|
+
.paywall-btn.primary{background:#3b82f6;color:#fff}
|
|
26
|
+
.paywall-btn.primary:hover{background:#2563eb;transform:translateY(-1px)}
|
|
27
|
+
.paywall-btn.secondary{background:transparent;color:#666;border:2px solid #e5e7eb}
|
|
28
|
+
.paywall-btn.secondary:hover{background:#f9fafb}
|
|
29
|
+
.footer{margin-top:24px;font-size:14px;color:#666}
|
|
30
|
+
.footer a{color:#3b82f6;text-decoration:none}
|
|
31
|
+
.footer a:hover{text-decoration:underline}
|
|
32
|
+
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
|
|
33
|
+
@keyframes slideUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}
|
|
34
|
+
`,document.head.appendChild(c),t.debug("Paywall styles injected")}document.body.appendChild(n);let u=n.querySelector("#paywall-subscribe");u&&(u.onclick=()=>{t.info("Subscribe button clicked",{url:e.subscribeUrl||a.subscribeUrl}),window.location.href=e.subscribeUrl||a.subscribeUrl});let f=n.querySelector("#paywall-close");f&&(f.onclick=()=>{t.info("Paywall closed by user"),n.remove()});let p=n.querySelector("#paywall-login");p&&(p.onclick=c=>{c.preventDefault(),t.info("Login link clicked",{url:a.loginUrl}),window.location.href=a.loginUrl}),l.measure("Modal created","modal_create_start"),t.success("Paywall modal displayed")}function b(){l.mark("blur_start");let e=[];if(a.protectBody)e=[document.body],t.info("Protecting entire page (body element)");else{let r=["main","article",'[role="main"]',".content",".post-content",".article-content",".entry-content","#content","#main-content"];t.debug("Searching for content elements",{selectors:r});for(let n of r){let u=document.querySelector(n);if(u){e=[u],t.info("Content element found",{selector:n});break}}e.length===0&&(e=[document.body],t.warn("No content elements found, protecting body as fallback"))}let o=0;e.forEach(r=>{r.style.filter="blur(8px)",r.style.userSelect="none",r.style.pointerEvents="none",r.addEventListener("copy",n=>{n.preventDefault(),t.debug("Copy attempt blocked")}),r.addEventListener("cut",n=>{n.preventDefault(),t.debug("Cut attempt blocked")}),r.addEventListener("contextmenu",n=>{n.preventDefault(),t.debug("Context menu blocked")}),o++}),l.measure("Content blurred","blur_start"),t.success("Content protection applied",{elementsProtected:o,protectionType:a.protectBody?"body":"selective"})}function v(){return k(this,null,function*(){if(document.readyState==="loading"){t.debug("DOM not ready, waiting for DOMContentLoaded"),document.addEventListener("DOMContentLoaded",v);return}if(t.info("DOM ready, starting protection sequence"),a.mode==="never"){t.warn("Widget disabled (mode=never)");return}if(h()){t.success("Allowed bot detected, skipping all protection");return}if(a.mode==="always"){t.info("Force mode enabled (mode=always)"),b(),w({type:"hard"});return}let e=yield P();t.info("Access check decision received",{allowed:e.allowed,status:e.status,showPaywall:e.showPaywall}),!e.allowed&&e.status==="payment_required"?(t.warn("Payment required for bot access",{paymentUrl:e.paymentUrl}),e.paymentUrl?window.location.href=e.paymentUrl:(b(),w({type:"bot-blocked",message:e.reason}))):!e.allowed&&e.status==="blocked"?(t.warn("Access blocked",{reason:e.reason}),b(),w({type:"bot-blocked",message:e.reason})):e.showPaywall&&e.paywallConfig?(t.info("Showing paywall to user",{type:e.paywallConfig.type}),b(),w(e.paywallConfig)):t.success("Access granted, no protection applied");let o=l.measure("Initialization complete","init_start");t.success("PaywallProtect initialization complete",{totalTime:o+"ms"})})}v(),window.PaywallProtect={version:"1.2.1",showPaywall:e=>{t.info("Manual showPaywall() called",e),b(),w(e||{type:"hard"})},hidePaywall:()=>{t.info("Manual hidePaywall() called");let e=document.getElementById("paywall-protect-modal");e?(e.remove(),t.success("Paywall hidden")):t.warn("No paywall to hide")},checkAccess:P,reload:()=>{t.info("Manual reload() called"),v()},config:a,allowedBots:i,logs:{getAll:()=>t.getAll(),export:()=>t.export(),clear:()=>t.clear(),download:()=>{let e=t.export(),o=new Blob([e],{type:"application/json"}),r=URL.createObjectURL(o),n=document.createElement("a");n.href=r,n.download=`paywall-logs-${t.sessionId}.json`,n.click(),URL.revokeObjectURL(r),t.info("Logs downloaded")}},debug:{enable:()=>{a.debug=!0,t.success("Debug mode enabled")},disable:()=>{a.debug=!1,t.info("Debug mode disabled")},status:()=>{console.table({Version:window.PaywallProtect.version,"Session ID":t.sessionId,"Site ID":a.siteId,Mode:a.mode,"SEO Safe":a.seoSafe,"Protect Body":a.protectBody,"Debug Mode":a.debug,"Allowed Bots":i.length,"Total Logs":t.logs.length,Uptime:Math.round(performance.now()-t.startTime)+"ms"})}}},t.success("Public API initialized",{methods:Object.keys(window.PaywallProtect)})})();})();
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gate-protect-widget",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "Gate widget for bot detection, content protection, and paywall management",
|
|
5
|
+
"main": "dist/paywall-widget.min.js",
|
|
6
|
+
"module": "dist/paywall-widget.esm.js",
|
|
7
|
+
"types": "types/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"types",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "npm run build:iife && npm run build:esm",
|
|
15
|
+
"build:iife": "esbuild src/index.js --bundle --minify --target=es2015 --format=iife --global-name=PaywallProtect --outfile=dist/paywall-widget.min.js",
|
|
16
|
+
"build:esm": "esbuild src/index.js --bundle --minify --target=es2015 --format=esm --outfile=dist/paywall-widget.esm.js",
|
|
17
|
+
"prepublishOnly": "npm run build"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/acvetne/gate-protect-widget.git"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"paywall",
|
|
25
|
+
"bot-detection",
|
|
26
|
+
"content-protection",
|
|
27
|
+
"anti-scraping",
|
|
28
|
+
"ai-bots",
|
|
29
|
+
"gate",
|
|
30
|
+
"security"
|
|
31
|
+
],
|
|
32
|
+
"author": "Gate Securities LLC",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/acvetne/gate-protect-widget/issues"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/acvetne/gate-protect-widget#readme",
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"esbuild": "^0.19.0"
|
|
40
|
+
}
|
|
41
|
+
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface PaywallProtectConfig {
|
|
2
|
+
siteId: string;
|
|
3
|
+
apiKey: string;
|
|
4
|
+
apiUrl?: string;
|
|
5
|
+
subscribeUrl?: string;
|
|
6
|
+
loginUrl?: string;
|
|
7
|
+
mode?: 'auto' | 'always' | 'never';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface PaywallConfig {
|
|
11
|
+
type: 'hard' | 'metered' | 'bot-blocked';
|
|
12
|
+
title?: string;
|
|
13
|
+
message?: string;
|
|
14
|
+
articlesRead?: number;
|
|
15
|
+
freeLimit?: number;
|
|
16
|
+
subscribeUrl?: string;
|
|
17
|
+
loginUrl?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface PaywallProtect {
|
|
21
|
+
version: string;
|
|
22
|
+
init(config: PaywallProtectConfig): void;
|
|
23
|
+
showPaywall(config?: PaywallConfig): void;
|
|
24
|
+
hidePaywall(): void;
|
|
25
|
+
reload(): void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
declare global {
|
|
29
|
+
interface Window {
|
|
30
|
+
PaywallProtect: PaywallProtect;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const PaywallProtect: PaywallProtect;
|
|
35
|
+
export default PaywallProtect;
|