Compare commits
2 Commits
a05bf6de74
...
6caf2dbcc3
| Author | SHA1 | Date | |
|---|---|---|---|
| 6caf2dbcc3 | |||
| fe1f318424 |
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@ -3,6 +3,7 @@
|
|||||||
"redhat.vscode-yaml",
|
"redhat.vscode-yaml",
|
||||||
"jetify.devbox",
|
"jetify.devbox",
|
||||||
"redhat.ansible",
|
"redhat.ansible",
|
||||||
"dotjoshjohnson.xml"
|
"dotjoshjohnson.xml",
|
||||||
|
"j69.ejs-beautify"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -59,4 +59,7 @@ In other words, pick something for a name and roll with the punches.
|
|||||||
|
|
||||||
### Do what you can, with what you have, where you are at
|
### Do what you can, with what you have, where you are at
|
||||||
|
|
||||||
### Use it up, wear it out, make it do, or do without
|
### Use it up, wear it out, make it do, or do without
|
||||||
|
|
||||||
|
### [Just Culture](https://pilotswhoaskwhy.com/2023/05/15/the-power-of-just-culture-killing-the-blame-game/)
|
||||||
|
|
||||||
|
|||||||
@ -80,6 +80,9 @@ model Vod {
|
|||||||
sourceAudioCodec String?
|
sourceAudioCodec String?
|
||||||
sourceVideoFps Float?
|
sourceVideoFps Float?
|
||||||
|
|
||||||
|
muxAssetId String?
|
||||||
|
muxPlaybackId String?
|
||||||
|
|
||||||
// audio analysis
|
// audio analysis
|
||||||
audioIntegratedLufs Float? // Integrated loudness (LUFS-I)
|
audioIntegratedLufs Float? // Integrated loudness (LUFS-I)
|
||||||
audioLoudnessRange Float? // Loudness Range (LRA)
|
audioLoudnessRange Float? // Loudness Range (LRA)
|
||||||
|
|||||||
5
services/pocketbase/.gitignore
vendored
Normal file
5
services/pocketbase/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.ftp-deploy-sync-state.json
|
||||||
|
node_modules
|
||||||
|
pb_data
|
||||||
|
.DS_Store
|
||||||
|
.secrets
|
||||||
1
services/pocketbase/CHANGELOG.md
Normal file
1
services/pocketbase/CHANGELOG.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
# pocketpages-starter-datastar
|
||||||
106
services/pocketbase/README.md
Normal file
106
services/pocketbase/README.md
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
# Datastar Starter Kit
|
||||||
|
|
||||||
|
This starter kit demonstrates how to build realtime web applications using [Datastar](https://github.com/starfederation/datastar) with PocketPages. It includes a live chat demo and counter example.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Realtime chat with emoji avatars
|
||||||
|
- Live counter with instant updates
|
||||||
|
- Datastar signal management
|
||||||
|
- Server-sent events (SSE) integration
|
||||||
|
- Modern UI with Tailwind CSS
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx tiged benallfree/pocketpages/packages/starters/datastar .
|
||||||
|
cd datastar
|
||||||
|
npm i
|
||||||
|
pocketbase serve --dir=pb_data --dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
The starter kit is pre-configured with the required Datastar setup:
|
||||||
|
|
||||||
|
1. **Plugin Configuration** (`+config.js`): Includes the datastar plugin
|
||||||
|
2. **Script Injection** (`+layout.ejs`): Includes `<%- datastar.scripts() %>` in the `<head>` section
|
||||||
|
3. **Realtime Integration**: Configured for realtime updates
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Start the server and visit `http://127.0.0.1:8090`
|
||||||
|
2. Open multiple browser tabs to see realtime updates
|
||||||
|
3. Send messages in the chat to see them appear instantly across all tabs
|
||||||
|
4. Click the counter button to see live updates
|
||||||
|
|
||||||
|
## Key Components
|
||||||
|
|
||||||
|
- **Chat System**: Real-time messaging with random emoji avatars
|
||||||
|
- **Counter**: Simple state management with live updates
|
||||||
|
- **Datastar Integration**: Server-side signal reading and client-side binding
|
||||||
|
- **Realtime Broadcasting**: Updates sent to all connected clients
|
||||||
|
|
||||||
|
## Datastar Features Demonstrated
|
||||||
|
|
||||||
|
- `datastar.patchElements()` - DOM updates
|
||||||
|
- `datastar.patchSignals()` - Signal management
|
||||||
|
- `datastar.readSignals()` - Form data reading
|
||||||
|
- `datastar.realtime.*` - Broadcasting to all clients
|
||||||
|
- Client-side data binding with `data-bind-*` attributes
|
||||||
|
- Event handling with `data-on-click` attributes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## dev notes
|
||||||
|
|
||||||
|
### S3 has some jenk
|
||||||
|
|
||||||
|
cache s3 requests at the proxy level
|
||||||
|
https://github.com/pocketbase/pocketbase/issues/28#issuecomment-1492516269
|
||||||
|
https://github.com/pocketbase/pocketbase/discussions/4332#discussioncomment-8450890
|
||||||
|
|
||||||
|
|
||||||
|
### search
|
||||||
|
|
||||||
|
https://www.meilisearch.com/
|
||||||
|
|
||||||
|
|
||||||
|
### horizontal scaling
|
||||||
|
|
||||||
|
https://github.com/fondoger/pocketbase (pg fork)
|
||||||
|
https://github.com/litesql/pocketbase-ha
|
||||||
|
|
||||||
|
|
||||||
|
### Data migrations
|
||||||
|
|
||||||
|
https://github.com/pocketbase/pocketbase/discussions/2836#discussioncomment-6340103
|
||||||
|
|
||||||
|
|
||||||
|
### FTS
|
||||||
|
|
||||||
|
https://github.com/pocketbuilds/fts?tab=readme-ov-file#readme
|
||||||
|
|
||||||
|
|
||||||
|
### plugins
|
||||||
|
|
||||||
|
https://pocketbuilds.com/
|
||||||
|
|
||||||
|
|
||||||
|
## Deployments
|
||||||
|
|
||||||
|
how the pros do it
|
||||||
|
|
||||||
|
https://github.com/benallfree/pocketpages/blob/5bc48d4f8df75b2f78ca61fa18c792d814b926e8/packages/starters/deploy-pockethost-manual/deploy-pockethost.ts#L5
|
||||||
|
|
||||||
|
## Avoid proxying files via pocketbase
|
||||||
|
|
||||||
|
https://github.com/pocketbase/pocketbase/discussions/5995
|
||||||
|
|
||||||
|
## realtime updates on db changes
|
||||||
|
|
||||||
|
https://github.com/pocketbase/pocketbase/discussions/4427#discussioncomment-8585118
|
||||||
|
|
||||||
|
## get a random item
|
||||||
|
|
||||||
|
https://github.com/pocketbase/pocketbase/discussions/2725
|
||||||
2897
services/pocketbase/package-lock.json
generated
Normal file
2897
services/pocketbase/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
services/pocketbase/package.json
Normal file
27
services/pocketbase/package.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "futureporn",
|
||||||
|
"version": "3.1.0",
|
||||||
|
"private": true,
|
||||||
|
"description": "Dedication to the preservation of lewdtuber history",
|
||||||
|
"license": "Unlicense",
|
||||||
|
"scripts": {
|
||||||
|
"deploy": "phio deploy"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"mime": "^4.1.0",
|
||||||
|
"nano-spawn": "^2.0.0",
|
||||||
|
"pg": "^8.16.3",
|
||||||
|
"pocketpages": ">=0.22.3",
|
||||||
|
"pocketpages-plugin-auth": "^0.2.2",
|
||||||
|
"pocketpages-plugin-datastar": ">=0.2.0",
|
||||||
|
"pocketpages-plugin-micro-dash": "^0.1.0"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"pb_hooks",
|
||||||
|
"*.md"
|
||||||
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"phio": "^0.3.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
107
services/pocketbase/pb_hooks/cdn.pb.js
Normal file
107
services/pocketbase/pb_hooks/cdn.pb.js
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* onFileDownloadRequest hook is triggered before each API File download request. Could be used to validate or modify the file response before returning it to the client.
|
||||||
|
* @see https://pocketbase.io/docs/js-event-hooks/#onfiledownloadrequest
|
||||||
|
*
|
||||||
|
* We use this to return a 302 to the CDN asset instead of having the asset proxied via Pocketbase
|
||||||
|
* @see https://github.com/pocketbase/pocketbase/discussions/5995
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
onFileDownloadRequest((e) => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// console.log('onFileDownloadRequest hook has been triggered ~~~');
|
||||||
|
// console.log('onFileDownloadRequest hook has been triggered ~~~');
|
||||||
|
// console.log('onFileDownloadRequest hook has been triggered ~~~');
|
||||||
|
// console.log('onFileDownloadRequest hook has been triggered ~~~');
|
||||||
|
// console.log('onFileDownloadRequest hook has been triggered ~~~');
|
||||||
|
// console.log('onFileDownloadRequest hook has been triggered ~~~');
|
||||||
|
// console.log('onFileDownloadRequest hook has been triggered ~~~');
|
||||||
|
// console.log('onFileDownloadRequest hook has been triggered ~~~');
|
||||||
|
// console.log('onFileDownloadRequest hook has been triggered ~~~');
|
||||||
|
// console.log('onFileDownloadRequest hook has been triggered ~~~');
|
||||||
|
// e.next()
|
||||||
|
|
||||||
|
// e.app
|
||||||
|
// e.collection
|
||||||
|
// e.record
|
||||||
|
// e.fileField
|
||||||
|
// e.servedPath
|
||||||
|
// e.servedName
|
||||||
|
// and all RequestEvent fields...
|
||||||
|
const securityKey = process.env?.BUNNY_TOKEN_KEY;
|
||||||
|
const baseUrl = process.env?.BUNNY_ZONE_URL;
|
||||||
|
|
||||||
|
console.log(`securityKey=${securityKey}, baseUrl=${baseUrl}`)
|
||||||
|
|
||||||
|
if (!securityKey) {
|
||||||
|
console.error('BUNNY_TOKEN_KEY was missing from env');
|
||||||
|
return e.next();
|
||||||
|
}
|
||||||
|
if (!baseUrl) {
|
||||||
|
console.error('BUNNY_ZONE_URL was missing from env');
|
||||||
|
return e.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a BunnyCDN-style signed URL using directory tokens.
|
||||||
|
*
|
||||||
|
* We sign URLs to make hotlinking difficult
|
||||||
|
* @see https://support.bunny.net/hc/en-us/articles/360016055099-How-to-sign-URLs-for-BunnyCDN-Token-Authentication
|
||||||
|
* @see https://github.com/pocketbase/pocketbase/discussions/5983#discussioncomment-11426659 // HMAC in pocketbase
|
||||||
|
* @see https://github.com/pocketbase/pocketbase/discussions/6772 // base64 encode the hex
|
||||||
|
*/
|
||||||
|
function signUrl(securityKey, baseUrl, path, expires) {
|
||||||
|
|
||||||
|
if (!path.startsWith('/')) path = '/' + path;
|
||||||
|
if (baseUrl.endsWith('/')) throw new Error(`baseUrl must not end with a slash. got baseUrl=${baseUrl}`);
|
||||||
|
|
||||||
|
|
||||||
|
const hashableBase = securityKey + path + expires;
|
||||||
|
|
||||||
|
// Generate and encode the token
|
||||||
|
const tokenH = $security.sha256(hashableBase);
|
||||||
|
|
||||||
|
const token = Buffer.from(tokenH, "hex")
|
||||||
|
.toString("base64")
|
||||||
|
.replace(/\n/g, "") // Remove newlines
|
||||||
|
.replace(/\+/g, "-") // Replace + with -
|
||||||
|
.replace(/\//g, "_") // Replace / with _
|
||||||
|
.replace(/=/g, ""); // Remove =
|
||||||
|
|
||||||
|
|
||||||
|
// Generate the URL
|
||||||
|
const signedUrl = baseUrl + path + '?token=' + token + '&expires=' + expires;
|
||||||
|
|
||||||
|
return signedUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`record: ${JSON.stringify(e.record)}`)
|
||||||
|
console.log(`collection: ${JSON.stringify(e.collection)}`)
|
||||||
|
console.log(`app: ${JSON.stringify(e.app)}`)
|
||||||
|
console.log(`fileField: ${JSON.stringify(e.fileField)}`)
|
||||||
|
console.log(`servedPath: ${JSON.stringify(e.servedPath)}`)
|
||||||
|
console.log(`servedName: ${JSON.stringify(e.servedName)}`)
|
||||||
|
|
||||||
|
// Our job here is to take the servedPath, and sign it using bunnycdn method
|
||||||
|
// Then serve a 302 redirect instead of serving the file proxied thru PB
|
||||||
|
|
||||||
|
const path = e.servedPath;
|
||||||
|
const expires = Math.round(Date.now() / 1000) + 3600;
|
||||||
|
const signedUrl = signUrl(securityKey, baseUrl, path, expires);
|
||||||
|
console.log(`signedUrl=${signedUrl}`);
|
||||||
|
|
||||||
|
// This redirect is a tricky thing. We do this to avoid proxying file requests via our pocketbase origin server.
|
||||||
|
// The idea is to reduce load.
|
||||||
|
// HOWEVER, this redirect slows down image loading because it now takes 2 requests per image.
|
||||||
|
e.redirect(302, signedUrl);
|
||||||
|
|
||||||
|
e.next()
|
||||||
|
})
|
||||||
37
services/pocketbase/pb_hooks/import.pb.js
Normal file
37
services/pocketbase/pb_hooks/import.pb.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
|
||||||
|
// @see https://github.com/pocketbase/pocketbase/discussions/6287
|
||||||
|
|
||||||
|
// NOTE: this script must be run from the project root
|
||||||
|
// ./pocketbase import /path/to/your/data.json YOUR_COLLECTION
|
||||||
|
$app.rootCmd.addCommand(new Command({
|
||||||
|
use: "import",
|
||||||
|
run: (cmd, args) => {
|
||||||
|
const data = require(args[0])
|
||||||
|
|
||||||
|
const collection = $app.findCollectionByNameOrId(args[1]);
|
||||||
|
// console.log(`data`, data, 'collection', collection);
|
||||||
|
|
||||||
|
$app.runInTransaction((txApp) => {
|
||||||
|
let record;
|
||||||
|
for (let item of data) {
|
||||||
|
// check https://pocketbase.io/docs/js-records/
|
||||||
|
record = new Record(collection)
|
||||||
|
|
||||||
|
// bulk set the item field values
|
||||||
|
//
|
||||||
|
// note: if you want to set predefined values for "autodate" type fields
|
||||||
|
// you'll need to manually load them with record.setRaw("created", val)
|
||||||
|
record.load(item)
|
||||||
|
|
||||||
|
// call txApp.saveNoValidate for better performance
|
||||||
|
// but be aware that the fields validators will be skipped and could result in data integrity issues
|
||||||
|
// if you haven't checked and verified the imported data beforehand
|
||||||
|
txApp.save(record)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
82
services/pocketbase/pb_hooks/pages/(site)/+layout.ejs
Normal file
82
services/pocketbase/pb_hooks/pages/(site)/+layout.ejs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" class="h-full">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>
|
||||||
|
<%=meta('title') || '~~~~~' %>
|
||||||
|
</title>
|
||||||
|
<meta name="description" content="<%=meta('description') || 'aaaaa'%>" />
|
||||||
|
<meta property="og:title" content="<%=meta('title') || 'Futureporn.net'%>" />
|
||||||
|
<meta property="og:description" content="<%=meta('description') %>" />
|
||||||
|
<meta property="og:image" content="<%=meta('image') || '/android-chrome-512x512.png'%>" />
|
||||||
|
<meta property="og:url" content="<%=meta('path') ? `${meta('path')}` : meta('url') || `${request.url}`%>" />
|
||||||
|
<meta name="RATING" content="RTA-5042-1996-1400-1577-RTA" />
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="alternate" type="application/rss+xml" title="Futureporn VODs (RSS)" href="/vods/feed.xml">
|
||||||
|
<link rel="alternate" type="application/rss+xml" title="Futureporn VODs (Atom)" href="/vods/rss.xml">
|
||||||
|
<link rel="alternate" type="application/json" title="Futureporn VODs" href="/vods/feed.json">
|
||||||
|
|
||||||
|
|
||||||
|
<%- slots.head %> <%- datastar.scripts({realtime: true}) %>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="h-full bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-100 font-sans antialiased">
|
||||||
|
<div class="min-h-full flex flex-col">
|
||||||
|
<main class="flex-1 container mx-auto px-4 py-8 sm:px-6 lg:px-8">
|
||||||
|
<div class="max-w-4xl mx-auto">
|
||||||
|
<div class="space-y-8">
|
||||||
|
<!-- Header Section -->
|
||||||
|
<nav class="level">
|
||||||
|
<!-- Left side -->
|
||||||
|
<div class="level-left">
|
||||||
|
<div class="level-item">
|
||||||
|
<p class="subtitle is-5"><a href="/"><strong>Futureporn.net</strong></a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="level-right">
|
||||||
|
<% if (auth) { %>
|
||||||
|
<div class="level-item">
|
||||||
|
<a href="/account">Account</a>
|
||||||
|
</div>
|
||||||
|
<div class="level-item">
|
||||||
|
<a href="/auth/logout">Logout</a>
|
||||||
|
</div>
|
||||||
|
<% } else { %>
|
||||||
|
<div class="level-item">
|
||||||
|
<a href="/auth/login">Login</a>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<%#
|
||||||
|
<div class="notification is-info" data-signals='{"showNotif": false}' data-show="$showNotif">
|
||||||
|
<button class="delete" data-on-click="$showNotif = false"></button>
|
||||||
|
<span data-text="$message"></span>
|
||||||
|
</div>
|
||||||
|
%>
|
||||||
|
|
||||||
|
|
||||||
|
<%- slots.body || slot%>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<footer class="footer mt-5">
|
||||||
|
<div class="content has-text-centered">
|
||||||
|
<p>
|
||||||
|
<strong>Futureporn <%= data.version %></strong> made with love by <a href="https://t.co/I8p0oH0AAB">@CJ_Clippy</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
31
services/pocketbase/pb_hooks/pages/(site)/+middleware.js
Normal file
31
services/pocketbase/pb_hooks/pages/(site)/+middleware.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/** @type {import('pocketpages').PageDataLoaderFunc} */
|
||||||
|
module.exports = function ({ meta, redirect, request, auth }) {
|
||||||
|
meta('title', 'Futureporn.net')
|
||||||
|
meta('description', 'Dedication to the preservation of Lewdtuber history')
|
||||||
|
meta('image', 'https://example.com/about-preview.jpg')
|
||||||
|
|
||||||
|
const cookies = request.cookies()
|
||||||
|
// console.log('cookies as follows')
|
||||||
|
// console.log(JSON.stringify(cookies))
|
||||||
|
// console.log('auth as follows')
|
||||||
|
// console.log(auth)
|
||||||
|
|
||||||
|
let user;
|
||||||
|
|
||||||
|
if (auth) {
|
||||||
|
console.log('request.auth is present id:', auth.get('id'))
|
||||||
|
user = $app.findFirstRecordByData('users', 'id', auth.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { user, version: require(`../../../package.json`).version }
|
||||||
|
}
|
||||||
|
|
||||||
|
// module.exports = (api, next) => {
|
||||||
|
// const { auth, redirect } = api
|
||||||
|
// if (!auth) {
|
||||||
|
// return redirect('/auth/login', {
|
||||||
|
// message: 'You must be logged in to access this page',
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// next()
|
||||||
|
// }
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
module.exports = (api, next) => {
|
||||||
|
const { auth, redirect } = api
|
||||||
|
if (!auth) {
|
||||||
|
return redirect('/auth/login', {
|
||||||
|
message: 'You must be logged in to access this page',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
}
|
||||||
37
services/pocketbase/pb_hooks/pages/(site)/account/index.ejs
Normal file
37
services/pocketbase/pb_hooks/pages/(site)/account/index.ejs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<h2 class="title is-2">Account Overview</h2>
|
||||||
|
<p>Welcome back, <strong><%= auth.get('name') %></strong>.</p>
|
||||||
|
|
||||||
|
<h3 class="title is-3 mt-5">Patreon Information</h3>
|
||||||
|
|
||||||
|
<div class="notification is-info is-light">
|
||||||
|
Please note: Patron status updates may take up to one minute to synchronize.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p><strong>Patreon ID:</strong> <%= auth.get('patreonId') %></p>
|
||||||
|
<p>
|
||||||
|
<strong>Role:</strong>
|
||||||
|
<% if (auth.get('patron')) { %>
|
||||||
|
Patron
|
||||||
|
<% } else { %>
|
||||||
|
Not a Patron
|
||||||
|
<a class="ml-3" target="_blank" href="https://patreon.com/cj_clippy">Become a Patron</a>
|
||||||
|
<% } %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<% if (auth.get('patron')) { %>
|
||||||
|
<div class="mt-5">
|
||||||
|
<h3 class="title is-3">Account Settings</h3>
|
||||||
|
|
||||||
|
<label class="checkbox">
|
||||||
|
<input class="checkbox" type="checkbox" name="publicUsername" data-bind="publicUsername" data-on-input__debounce.300ms.leading="@patch('/api/user/settings')">
|
||||||
|
Show <%= auth.get('name') %> on the <a href="/patrons">patrons page</a>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div id="results" class="mt-5"></div>
|
||||||
|
|
||||||
|
<!-- <br />
|
||||||
|
<input class="button" type="submit" value="Save" /> -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
<script server>
|
||||||
|
let error = null
|
||||||
|
const methods = pb().collection('users').listAuthMethods();
|
||||||
|
dbg({
|
||||||
|
methods
|
||||||
|
})
|
||||||
|
|
||||||
|
const {
|
||||||
|
providers
|
||||||
|
} = methods.oauth2
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<% if (error) { %>
|
||||||
|
<mark><%= error %></mark>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (!methods.oauth2.enabled) { %>
|
||||||
|
<mark>OAuth2 login is disabled. See README.md for details.</mark>
|
||||||
|
<% } else {%>
|
||||||
|
<% providers.forEach(provider => { %>
|
||||||
|
<form method="POST" action="/auth/oauth/login">
|
||||||
|
<input type="hidden" name="provider" value="<%=provider.name%>">
|
||||||
|
<button class="button" type="submit">Log in with <%=provider.displayName%></button>
|
||||||
|
</form>
|
||||||
|
<% }) %>
|
||||||
|
<% if (providers.length === 0) { %>
|
||||||
|
<mark>No OAuth2 providers enabled. See README.md for details.</mark>
|
||||||
|
<% } %>
|
||||||
|
<% } %>
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
<h2 class="title is-2">Login</h2>
|
||||||
|
|
||||||
|
<p>Log in to Futureporn using a Patreon account.</p>
|
||||||
|
<p>Logging in provides access to your patron perks.</p>
|
||||||
|
<p>To become a patron, visit <a href="https://patreon.com/cj_clippy">CJ_Clippy on Patreon</a>.</p>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
<%- include('oauth2.ejs') %>
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
<script server>
|
||||||
|
signOut()
|
||||||
|
redirect(`/`)
|
||||||
|
</script>
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
<script server>
|
||||||
|
const {
|
||||||
|
state,
|
||||||
|
code
|
||||||
|
} = params
|
||||||
|
|
||||||
|
let error = null
|
||||||
|
let authData = null
|
||||||
|
try {
|
||||||
|
authData = signInWithOAuth2(state, code)
|
||||||
|
|
||||||
|
|
||||||
|
dbg("the shit user id is " + authData.record.id)
|
||||||
|
if (!authData.record.id) {
|
||||||
|
throw new Error('invalid authData. authData must contain a record.id');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!authData.record.created) {
|
||||||
|
throw new Error('invalid authData. missing record.created')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let user = $app.findRecordById('users', authData.record.id)
|
||||||
|
|
||||||
|
|
||||||
|
// if user is new, give them a free trial
|
||||||
|
const userCreatedAt = new Date(authData.record.created).valueOf();
|
||||||
|
const anHourAgo = Date.now() - 60 * 60 * 1000; // 1 hour in ms
|
||||||
|
dbg(`comparing ${userCreatedAt} > ${anHourAgo}`);
|
||||||
|
if (userCreatedAt > anHourAgo) {
|
||||||
|
dbg("THE USER IS NEW");
|
||||||
|
user.set('patron', true);
|
||||||
|
} else {
|
||||||
|
dbg("the user is NOT new");
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(JSON.stringify(authData, null, 2))
|
||||||
|
dbg("authData:", stringify(authData))
|
||||||
|
|
||||||
|
|
||||||
|
user.set('patreonAccessToken', authData.meta.accessToken);
|
||||||
|
// user.set('patreonId', authData.meta.id); // we do this via the admin UI
|
||||||
|
$app.save(user);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// patreon id can be found in _externalAuths system table
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Here we set the patreonId.
|
||||||
|
// If we're seeing this user for the first time,
|
||||||
|
// unconditionally grant them patron status.
|
||||||
|
// The backend later removes the status if they aren't subscribed
|
||||||
|
// let user = $app.findRecordById('user', )
|
||||||
|
|
||||||
|
response.redirect('/')
|
||||||
|
} catch (e) {
|
||||||
|
error = e.message
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% if (error) { %>
|
||||||
|
<mark>
|
||||||
|
<%=error%>
|
||||||
|
</mark>
|
||||||
|
<% } %>
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
<script server>
|
||||||
|
let error = null
|
||||||
|
|
||||||
|
if (request.method === 'POST') {
|
||||||
|
let provider
|
||||||
|
try {
|
||||||
|
provider = body().provider
|
||||||
|
const url = requestOAuth2Login(provider)
|
||||||
|
console.log('url is as follows')
|
||||||
|
console.log(url)
|
||||||
|
response.redirect(url)
|
||||||
|
} catch (error) {
|
||||||
|
console.log("FAILURE! We failed to get the OAuth2Login from provider", provider)
|
||||||
|
error = error.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<% if (error) { %>
|
||||||
|
<p>we got an err</p>
|
||||||
|
<mark>
|
||||||
|
<%=error%>
|
||||||
|
</mark>
|
||||||
|
<% } %>
|
||||||
10
services/pocketbase/pb_hooks/pages/(site)/humans.txt
Normal file
10
services/pocketbase/pb_hooks/pages/(site)/humans.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
Hello humans
|
||||||
|
|
||||||
|
futureporn.net
|
||||||
|
future.porn
|
||||||
|
|
||||||
|
R18. For adults only.
|
||||||
|
|
||||||
|
Dedication to the preservation of VTuber history
|
||||||
|
|
||||||
|
made with love by CJ_Clippy cj@futureporn.net
|
||||||
23
services/pocketbase/pb_hooks/pages/(site)/index.ejs
Normal file
23
services/pocketbase/pb_hooks/pages/(site)/index.ejs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<% if (!data?.user?.get('patron')) { %>
|
||||||
|
<div class="notification is-info">
|
||||||
|
Thank you for your support on <a href="https://patreon.com/cj_clippy">Patreon!</a>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<ul class="mt-5">
|
||||||
|
<li class="mb-2">
|
||||||
|
<a class="button" href="/vt">
|
||||||
|
<h2 class="title is-2">VTubers</h2>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="mb-2">
|
||||||
|
<a class="button" href="/vods">
|
||||||
|
<h2 class="title is-2">Vods</h2>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="mb-2">
|
||||||
|
<a class="button" href="/patrons">
|
||||||
|
<h2 class="title is-2">Patrons</h2>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
37
services/pocketbase/pb_hooks/pages/(site)/patrons/+load.js
Normal file
37
services/pocketbase/pb_hooks/pages/(site)/patrons/+load.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// +load.js
|
||||||
|
|
||||||
|
/** @type {import('pocketpages').PageDataLoaderFunc} */
|
||||||
|
module.exports = function (api) {
|
||||||
|
const { response } = api;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Find all users who have both a publicUsername and are patrons
|
||||||
|
const patronsRaw = $app.findRecordsByFilter(
|
||||||
|
'users',
|
||||||
|
'publicUsername != "" && patron = true',
|
||||||
|
'-created', // sort (optional)
|
||||||
|
50, // limit
|
||||||
|
0, // offset
|
||||||
|
null // params (none used)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Map to plain JSON-safe objects
|
||||||
|
const patrons = patronsRaw.map((p) => ({
|
||||||
|
name: p.get('name'),
|
||||||
|
username: p.get('publicUsername'),
|
||||||
|
}));
|
||||||
|
|
||||||
|
console.log('Patrons:', JSON.stringify(patrons, null, 2));
|
||||||
|
|
||||||
|
return { patrons };
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error('error!', e.message);
|
||||||
|
|
||||||
|
if (e.message.match(/no rows/)) {
|
||||||
|
return response.html(404, 'Patrons not found');
|
||||||
|
} else {
|
||||||
|
return response.html(500, 'Unknown internal error while fetching patrons');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
19
services/pocketbase/pb_hooks/pages/(site)/patrons/index.ejs
Normal file
19
services/pocketbase/pb_hooks/pages/(site)/patrons/index.ejs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<p></p>
|
||||||
|
|
||||||
|
<div class="menu">
|
||||||
|
<p class="subtitle">
|
||||||
|
Thank you to our wonderful patrons who keep this site running 💖
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<% if (Array.isArray(data.patrons) && data.patrons.length > 0) { %>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<% for (const patron of data.patrons) { %>
|
||||||
|
<li>
|
||||||
|
<%= patron.name %>
|
||||||
|
</li>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
|
<% } else { %>
|
||||||
|
<p class="has-text-grey-light">Patron names are private by default. Become the first supporter and <a href="/account#settings">opt-in</a> to have your name shown!</p>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
10
services/pocketbase/pb_hooks/pages/(site)/robots.txt
Normal file
10
services/pocketbase/pb_hooks/pages/(site)/robots.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# robots.txt for futureporn.net
|
||||||
|
|
||||||
|
# Block all web crawlers from accessing admin and private areas
|
||||||
|
User-agent: *
|
||||||
|
Disallow: /_/
|
||||||
|
Disallow: /auth/
|
||||||
|
Disallow: /api/
|
||||||
|
|
||||||
|
# Allow crawlers to access public content
|
||||||
|
Allow: /
|
||||||
30
services/pocketbase/pb_hooks/pages/(site)/vods/+load.js
Normal file
30
services/pocketbase/pb_hooks/pages/(site)/vods/+load.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {import('pocketbase').default} PocketBase
|
||||||
|
* @typedef {import('../pb/pocketbase-types').TypedPocketBase} TypedPocketBase
|
||||||
|
* @typedef {import('pocketpages').PageDataLoaderFunc} PageDataLoaderFunc
|
||||||
|
*/
|
||||||
|
module.exports = function (api) {
|
||||||
|
const { request, response, params, pb } = api;
|
||||||
|
try {
|
||||||
|
const perPage = params.perPage || 25;
|
||||||
|
const page = params.page || 1;
|
||||||
|
|
||||||
|
const client = pb({ request });
|
||||||
|
const vods = client.collection('vods').getList(page, perPage, {
|
||||||
|
expand: 'vtubers',
|
||||||
|
sort: '-streamDate'
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
return { vods };
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error fetching VODs:', e.message);
|
||||||
|
|
||||||
|
if (e.message.match(/no rows/)) {
|
||||||
|
return response.html(404, 'VODs not found');
|
||||||
|
} else {
|
||||||
|
return response.html(500, 'Unknown internal error while fetching VODs');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
26
services/pocketbase/pb_hooks/pages/(site)/vods/[id]/+load.js
Normal file
26
services/pocketbase/pb_hooks/pages/(site)/vods/[id]/+load.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// +load.js
|
||||||
|
|
||||||
|
/** @type {import('pocketpages').PageDataLoaderFunc} */
|
||||||
|
module.exports = function (api) {
|
||||||
|
const { params, response } = api;
|
||||||
|
try {
|
||||||
|
const vod = $app.findFirstRecordByData('vods', 'id', params.id);
|
||||||
|
$app.expandRecord(vod, ["vtubers"], null);
|
||||||
|
|
||||||
|
|
||||||
|
return { vod };
|
||||||
|
} catch (e) {
|
||||||
|
console.error('error!', e.message);
|
||||||
|
|
||||||
|
if (e.message.match(/no rows/)) {
|
||||||
|
console.log('we are sending 404')
|
||||||
|
return response.html(404, 'VOD not found')
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.log('we are sending error 500')
|
||||||
|
return response.html(500, 'Unknown internal error while fetching vod')
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
<div class="players" <% if (data?.user?.get('patron')) { %> data-signals="{'selected':'cdn1'}" <% } else { %> data-signals="{'selected':'cdn2'}" <% } %>>
|
||||||
|
|
||||||
|
<!-- CDN2 Player (B2) -->
|
||||||
|
<div class="b2-player" data-show="$selected == 'cdn2'">
|
||||||
|
<video controls <% if (!data?.user?.get('patron')) { %> preload="none" <% } %>>
|
||||||
|
<% if (data.vod?.get('videoSrcB2')) { %>
|
||||||
|
<source src="<%= data.vod?.get('videoSrcB2') %>" type="video/mp4">
|
||||||
|
<% } else { %>
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
<% } %>
|
||||||
|
</video>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- CDN1 Player (Mux, patrons only) -->
|
||||||
|
<% if (data?.user?.get('patron')) { %>
|
||||||
|
<div class="mux-player" data-show="$selected == 'cdn1'">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@mux/mux-player" defer></script>
|
||||||
|
<mux-player playback-id="<%= data.vod?.get('muxPlaybackId') %>" playback-token="<%= data.vod?.get('muxPlaybackToken') %>"></mux-player>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Player toggle buttons -->
|
||||||
|
<nav class="level mt-5">
|
||||||
|
<div class="level-left">
|
||||||
|
<% if (data?.user?.get('patron')) { %>
|
||||||
|
<button class="button is-success" data-on-click="$selected = 'cdn1'">CDN1 player</button>
|
||||||
|
<% } else { %>
|
||||||
|
<button disabled class="button is-danger">CDN1 player (patrons only)</button>
|
||||||
|
<% } %>
|
||||||
|
<button class="button is-success" data-on-click="$selected = 'cdn2'">CDN2 player</button>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- VOD Details -->
|
||||||
|
<div class="vod-details box mt-5">
|
||||||
|
<p><b>VOD ID:</b> <%= data.vod?.id ?? 'N/A' %></p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<b>VTubers:</b>
|
||||||
|
<% const vtubers = data.vod?.get('expand')?.vtubers ?? []; %>
|
||||||
|
<% vtubers.forEach((vtuber, index) => { %>
|
||||||
|
<% if (vtuber) { %>
|
||||||
|
<a href="/vt/<%= vtuber.get('slug') ?? '#' %>">
|
||||||
|
<%= vtuber.get('displayName') ?? 'Unknown VTuber' %>
|
||||||
|
</a><%= index < vtubers.length - 1 ? ', ' : '' %>
|
||||||
|
<% } %>
|
||||||
|
<% }) %>
|
||||||
|
<% if (vtubers.length === 0) { %>None<% } %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b>Stream Date:</b> <%= data.vod?.get('streamDate') ?? 'Unknown date' %></p>
|
||||||
|
|
||||||
|
<% if (data.vod?.get('announceUrl')) { %>
|
||||||
|
<p>
|
||||||
|
<b>Announce URL:</b>
|
||||||
|
<a href="<%= data.vod?.get('announceUrl') %>"><%= data.vod?.get('announceUrl') %></a>
|
||||||
|
</p>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (data.vod?.get('ipfsCid')) { %>
|
||||||
|
<p><b>IPFS CID:</b> <%= data.vod?.get('ipfsCid') %></p>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (data.vod?.get('notes')) { %>
|
||||||
|
<p><b>Notes:</b></p>
|
||||||
|
<pre class="p-2"><%= data.vod?.get('notes') %></pre>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (data.vod?.get('thumbnail')) { %>
|
||||||
|
<p><b>Thumbnail:</b></p>
|
||||||
|
<figure class="image">
|
||||||
|
<img src="/api/files/vods/<%= data.vod?.get('id') %>/<%= data.vod?.get('thumbnail') %>" />
|
||||||
|
</figure>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-5"></div>
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
// +load.js
|
||||||
|
|
||||||
|
/** @type {import('pocketpages').PageDataLoaderFunc} */
|
||||||
|
module.exports = function (api) {
|
||||||
|
const { params, response } = api;
|
||||||
|
try {
|
||||||
|
const vods = $app.findRecordsByFilter('vods', null, '-streamDate');
|
||||||
|
$app.expandRecords(vods, ["vtubers"], null);
|
||||||
|
// vods.expandedAll("vtubers");
|
||||||
|
return { vods };
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error('error!', e.message);
|
||||||
|
|
||||||
|
if (e.message.match(/no rows/)) {
|
||||||
|
console.log('we are sending 404')
|
||||||
|
return response.html(404, 'VODs not found')
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.log('we are sending error 500')
|
||||||
|
return response.html(500, 'Unknown internal error while fetching vods')
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
<%#
|
||||||
|
index.ejs — Atom Feed Generator (Atom 1.0)
|
||||||
|
Expects: data.vods = [{ id, title, summary, streamDate }]
|
||||||
|
%>
|
||||||
|
|
||||||
|
<%
|
||||||
|
const siteBase = "https://futureporn.net";
|
||||||
|
const feedSelf = `${siteBase}/vods/feed.xml`;
|
||||||
|
const updated = new Date().toISOString();
|
||||||
|
response.header('Content-Type', 'application/atom+xml')
|
||||||
|
%>
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||||
|
<id><%= siteBase %></id>
|
||||||
|
<title>Futureporn.net</title>
|
||||||
|
<updated><%= updated %></updated>
|
||||||
|
<generator> </generator>
|
||||||
|
|
||||||
|
<author>
|
||||||
|
<name>CJ_Clippy</name>
|
||||||
|
<email>cj@futureporn.net</email>
|
||||||
|
<uri><%= siteBase %></uri>
|
||||||
|
</author>
|
||||||
|
|
||||||
|
<link rel="alternate" href="<%= siteBase %>" />
|
||||||
|
<link rel="self" href="<%= feedSelf %>" />
|
||||||
|
|
||||||
|
<subtitle><%= meta('description') %></subtitle>
|
||||||
|
<logo><%= siteBase %>/images/futureporn-icon.png</logo>
|
||||||
|
<icon><%= siteBase %>/favicon.ico</icon>
|
||||||
|
|
||||||
|
<% for (const vod of data.vods) { %>
|
||||||
|
<%
|
||||||
|
const url = `${siteBase}/vods/${vod.id}`;
|
||||||
|
%>
|
||||||
|
<entry>
|
||||||
|
<title type="html">
|
||||||
|
<![CDATA[ <%= vod.title %> ]]>
|
||||||
|
</title>
|
||||||
|
<id><%= url %></id>
|
||||||
|
<link href="<%= url %>" />
|
||||||
|
<updated><%= new Date(vod.updated) %></updated>
|
||||||
|
<% if (vod.notes) { %>
|
||||||
|
<summary type="html">
|
||||||
|
<![CDATA[ <%= vod.notes %> ]]>
|
||||||
|
</summary>
|
||||||
|
<% } %>
|
||||||
|
</entry>
|
||||||
|
<% } %>
|
||||||
|
</feed>
|
||||||
3
services/pocketbase/pb_hooks/pages/(site)/vods/index.ejs
Normal file
3
services/pocketbase/pb_hooks/pages/(site)/vods/index.ejs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<h2 class="title is-2">VODs</h2>
|
||||||
|
|
||||||
|
<%- include('vod-list.ejs', data) %>
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
// +middleware.js
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** @type {import('pocketpages').PageDataLoaderFunc} */
|
||||||
|
module.exports = function (api) {
|
||||||
|
const { params, request, pb } = api;
|
||||||
|
|
||||||
|
const perPage = params.perPage || 25;
|
||||||
|
const page = params.page || 1;
|
||||||
|
const vtuber = $app.findFirstRecordByData('vtubers', 'slug', params.slug);
|
||||||
|
const client = pb({ request });
|
||||||
|
const vods = client.collection('vods').getList(page, perPage, {
|
||||||
|
expand: 'vtubers',
|
||||||
|
sort: '-streamDate',
|
||||||
|
filter: `vtubers.id ?= "${vtuber.id}"`
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// await pb.collection("posts").getList(1, 30, {
|
||||||
|
// filter: "comments_via_post.message ?~ 'hello'"
|
||||||
|
// expand: "comments_via_post.user",
|
||||||
|
// })
|
||||||
|
|
||||||
|
// console.log(JSON.stringify(vods, null, 2));
|
||||||
|
|
||||||
|
// findRecordsByFilter(collectionModelOrIdentifier, filter, sort, limit, offset, ...params): core.Record[]
|
||||||
|
// $app.expandRecord(vtuber, ['vods_via_vtubers'], null);
|
||||||
|
|
||||||
|
|
||||||
|
// $app.expandRecord(vod, ["vtubers"], null);
|
||||||
|
// console.log(eerrs)
|
||||||
|
|
||||||
|
|
||||||
|
return { vods, vtuber };
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
@ -0,0 +1 @@
|
|||||||
|
<%- include('vtuber.ejs', data) %>
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
<h2 class="title is-2"><%= data.vtuber.get('displayName') %> VODs</h2>
|
||||||
|
|
||||||
|
<%- include('vod-list.ejs', data) %>
|
||||||
129
services/pocketbase/pb_hooks/pages/(site)/vt/_private/vtuber.ejs
Normal file
129
services/pocketbase/pb_hooks/pages/(site)/vt/_private/vtuber.ejs
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
<div id="vtuber" class="">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
|
||||||
|
<section class="section">
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column">
|
||||||
|
<%# VTuber Name %>
|
||||||
|
<h2 class="title is-2">
|
||||||
|
<%= data.vtuber?.get?.('displayName') || 'Unknown VTuber' %>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
|
<%# VTuber Image %>
|
||||||
|
<figure class="image is-128x128">
|
||||||
|
<img src="/api/files/vtubers/<%= data.vtuber?.id %>/<%= data.vtuber?.get('image') %>?thumb=128x128" alt="<%= data.vtuber?.get?.('displayName') || 'VTuber' %>" />
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
|
||||||
|
|
||||||
|
<h3 class="title is-3">Theme Color</h3>
|
||||||
|
<% const themeColor = data.vtuber?.get('themeColor') || '#999999' %>
|
||||||
|
|
||||||
|
<div class="theme-color" style="
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
background-color: <%= themeColor %>
|
||||||
|
" title="<%= themeColor %>"></div>
|
||||||
|
|
||||||
|
<p class="has-text-grey is-size-7 mb-5"><%= themeColor %></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
<% const socials = {
|
||||||
|
chaturbate: data.vtuber?.get('chaturbate'),
|
||||||
|
twitter: data.vtuber?.get('twitter'),
|
||||||
|
patreon: data.vtuber?.get('patreon'),
|
||||||
|
twitch: data.vtuber?.get('twitch'),
|
||||||
|
tiktok: data.vtuber?.get('tiktok'),
|
||||||
|
onlyfans: data.vtuber?.get('onlyfans'),
|
||||||
|
youtube: data.vtuber?.get('youtube'),
|
||||||
|
linktree: data.vtuber?.get('linktree'),
|
||||||
|
carrd: data.vtuber?.get('carrd'),
|
||||||
|
fansly: data.vtuber?.get('fansly'),
|
||||||
|
pornhub: data.vtuber?.get('pornhub'),
|
||||||
|
discord: data.vtuber?.get('discord'),
|
||||||
|
reddit: data.vtuber?.get('reddit'),
|
||||||
|
throne: data.vtuber?.get('throne'),
|
||||||
|
instagram: data.vtuber?.get('instagram'),
|
||||||
|
facebook: data.vtuber?.get('facebook'),
|
||||||
|
merch: data.vtuber?.get('merch')
|
||||||
|
} %>
|
||||||
|
|
||||||
|
<% const definedSocials = Object.entries(socials).filter(([_, v]) => v) %>
|
||||||
|
|
||||||
|
<% if (definedSocials.length > 0) { %>
|
||||||
|
<section class="section">
|
||||||
|
<h3 class="title is-3">Socials</h3>
|
||||||
|
<div class="tags are-medium">
|
||||||
|
<% for (const [name, url] of definedSocials) { %>
|
||||||
|
<a href="<%= url %>" class="tag is-link is-light" target="_blank" rel="noopener">
|
||||||
|
<%= name.charAt(0).toUpperCase() + name.slice(1) %>
|
||||||
|
</a>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
|
||||||
|
<section class="section">
|
||||||
|
|
||||||
|
<h3 class="title is-3">VODs</h3>
|
||||||
|
|
||||||
|
|
||||||
|
<%
|
||||||
|
const vods = data.vods.items
|
||||||
|
if (vods.length > 0) {
|
||||||
|
%>
|
||||||
|
<div class="columns is-multiline">
|
||||||
|
<% for (const vod of vods) { %>
|
||||||
|
<div class="column is-one-quarter-desktop is-half-tablet is-full-mobile">
|
||||||
|
<a href="/vods/<%= vod.id %>" class="box has-text-centered">
|
||||||
|
|
||||||
|
<!-- Thumbnail -->
|
||||||
|
<% if (vod.thumbnail) { %>
|
||||||
|
<figure class="image is-16by9 mb-2">
|
||||||
|
<img src="/api/files/vods/<%= vod.id %>/<%= vod.thumbnail %>?thumb=400x225" alt="VOD thumbnail for <%= vod.id %>" />
|
||||||
|
</figure>
|
||||||
|
<% } else { %>
|
||||||
|
<div class="has-background-grey-lighter py-6 mb-2">
|
||||||
|
<span class="has-text-grey">No thumbnail</span>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<!-- Title / ID -->
|
||||||
|
<p class="is-size-6 has-text-weight-semibold">
|
||||||
|
<%= vod.title || vod.id %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Date -->
|
||||||
|
<% if (vod.streamDate) { %>
|
||||||
|
<p class="is-size-7 has-text-grey">
|
||||||
|
<%= new Date(vod.streamDate).toLocaleDateString() %>
|
||||||
|
</p>
|
||||||
|
<% } %>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
<a href="<%= request.url.pathname.replace(/\/$/, '') %>/vods">See all <%= data.vtuber?.get?.('displayName') %> vods</a>
|
||||||
|
<% } else { %>
|
||||||
|
<p>No VODs available for this VTuber.</p>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
8
services/pocketbase/pb_hooks/pages/(site)/vt/index.ejs
Normal file
8
services/pocketbase/pb_hooks/pages/(site)/vt/index.ejs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<script server>
|
||||||
|
const
|
||||||
|
vtubers = $app.findRecordsByFilter('vtubers')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<h3>Vtubers</h3>
|
||||||
|
<%- include('vtuber-list.ejs', { vtubers }) %>
|
||||||
14
services/pocketbase/pb_hooks/pages/+config.js
Normal file
14
services/pocketbase/pb_hooks/pages/+config.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
debug: false,
|
||||||
|
plugins: [
|
||||||
|
'pocketpages-plugin-ejs',
|
||||||
|
'pocketpages-plugin-datastar',
|
||||||
|
'pocketpages-plugin-realtime',
|
||||||
|
'pocketpages-plugin-auth',
|
||||||
|
'pocketpages-plugin-js-sdk',
|
||||||
|
'pocketpages-plugin-micro-dash',
|
||||||
|
'../../../src/plugins/patreon'
|
||||||
|
],
|
||||||
|
}
|
||||||
79
services/pocketbase/pb_hooks/pages/_private/vod-list.ejs
Normal file
79
services/pocketbase/pb_hooks/pages/_private/vod-list.ejs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<% if (Array.isArray(data.vods.items) && data.vods.items.length > 0) { %>
|
||||||
|
<div class="table-container">
|
||||||
|
<table class="table is-striped is-hoverable is-fullwidth">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Stream Date</th>
|
||||||
|
<th>VTuber</th>
|
||||||
|
<th>Thumbnail</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% for (const vod of data.vods.items) { %>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="/vods/<%= vod.id %>" class="is-small is-link">
|
||||||
|
<%= vod.streamDate ? new Date(vod.streamDate).toLocaleString('en-US', { dateStyle: 'medium', timeStyle: 'short' }) : 'Unknown date' %>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<% const vtubers = vod.expand?.vtubers || []; %>
|
||||||
|
<% if (vtubers.length) { %>
|
||||||
|
<% vtubers.forEach(function(v, i){ %>
|
||||||
|
<a href="/vt/<%= v.slug %>" class="is-small"><%= v.displayName %></a><%= (i === vtubers.length - 2 ? (vtubers.length > 2 ? ', and ' : ' and ') : (i === vtubers.length - 1 ? '' : ', ')) %>
|
||||||
|
<% }) %>
|
||||||
|
<% } else { %>
|
||||||
|
<span>Unknown</span>
|
||||||
|
<% } %>
|
||||||
|
</td>
|
||||||
|
<td style="width: 160px;">
|
||||||
|
<% if (vod.thumbnail) { %>
|
||||||
|
<figure class="image is-3by2">
|
||||||
|
<img src="/api/files/<%= vod.collectionId %>/<%= vod.id %>/<%= vod.thumbnail %>" alt="Thumbnail" style="width: 120px; border-radius: 8px;">
|
||||||
|
</figure>
|
||||||
|
<% } else { %>
|
||||||
|
<span>No thumbnail</span>
|
||||||
|
<% } %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<% } %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pagination Controls -->
|
||||||
|
<% if (data.vods.totalPages > 1) { %>
|
||||||
|
<nav class="pagination is-centered" role="navigation" aria-label="pagination">
|
||||||
|
<% const currentPage = data.vods.page; %>
|
||||||
|
<% const totalPages = data.vods.totalPages; %>
|
||||||
|
|
||||||
|
<!-- Previous Page -->
|
||||||
|
<% if (currentPage > 1) { %>
|
||||||
|
<a class="pagination-previous" href="?page=<%= currentPage - 1 %>&perPage=<%= data.vods.perPage %>">Previous</a>
|
||||||
|
<% } else { %>
|
||||||
|
<a class="pagination-previous" disabled>Previous</a>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<!-- Next Page -->
|
||||||
|
<% if (currentPage < totalPages) { %>
|
||||||
|
<a class="pagination-next" href="?page=<%= currentPage + 1 %>&perPage=<%= data.vods.perPage %>">Next</a>
|
||||||
|
<% } else { %>
|
||||||
|
<a class="pagination-next" disabled>Next</a>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<!-- Page Numbers -->
|
||||||
|
<ul class="pagination-list">
|
||||||
|
<% for (let i = 1; i <= totalPages; i++) { %>
|
||||||
|
<li>
|
||||||
|
<a class="pagination-link <%= i === currentPage ? 'is-current' : '' %>" href="?page=<%= i %>&perPage=<%= data.vods.perPage %>">
|
||||||
|
<%= i %>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% } else { %>
|
||||||
|
<p>No VODs available.</p>
|
||||||
|
<% } %>
|
||||||
11
services/pocketbase/pb_hooks/pages/_private/vtuber-list.ejs
Normal file
11
services/pocketbase/pb_hooks/pages/_private/vtuber-list.ejs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<div id="vtuber-list">
|
||||||
|
<% for (const vtuber of vtubers) { %>
|
||||||
|
<div class="">
|
||||||
|
<span class="">
|
||||||
|
<a href="/vt/<%= vtuber.get('slug') %>">
|
||||||
|
<%= vtuber.get('displayName') %>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
<%
|
||||||
|
response.header("Content-Type",
|
||||||
|
"application/json");
|
||||||
|
response.html(410,
|
||||||
|
"");
|
||||||
|
%>
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
<script server>
|
||||||
|
// const {
|
||||||
|
// request,
|
||||||
|
// response
|
||||||
|
// } = api;
|
||||||
|
|
||||||
|
|
||||||
|
const cookies = request.cookies()
|
||||||
|
|
||||||
|
let user;
|
||||||
|
|
||||||
|
if (auth) {
|
||||||
|
console.log('request.auth is present id:', auth.get('id'))
|
||||||
|
user = $app.findFirstRecordByData('users', 'id', auth.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
datastar.patchSignals(stringify({
|
||||||
|
showNotif: true
|
||||||
|
}))
|
||||||
|
return response.html(401, "Auth required")
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('signals as follows')
|
||||||
|
const signals = datastar.readSignals(request, {})
|
||||||
|
|
||||||
|
user.set('publicUsername', signals.publicUsername);
|
||||||
|
|
||||||
|
// Determine the publicUsername status
|
||||||
|
const publicStatus = user.get('publicUsername') ?
|
||||||
|
'Your username is now <b>public</b>.' :
|
||||||
|
'Your username is now <b>private</b>.';
|
||||||
|
|
||||||
|
// Update the page with a verbose message
|
||||||
|
datastar.patchElements(`
|
||||||
|
<div id="results" class="notification is-success mt-5">
|
||||||
|
Saved! ${publicStatus}
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
</script>
|
||||||
6
services/pocketbase/pb_hooks/pages/api/v1.json/index.ejs
Normal file
6
services/pocketbase/pb_hooks/pages/api/v1.json/index.ejs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<%
|
||||||
|
response.header("Content-Type",
|
||||||
|
"application/json");
|
||||||
|
response.html(410,
|
||||||
|
"");
|
||||||
|
%>
|
||||||
@ -0,0 +1 @@
|
|||||||
|
<% response.redirect('/vods/feed.json', 301) %>
|
||||||
@ -0,0 +1 @@
|
|||||||
|
<% response.redirect('/vods/feed.xml', 301) %>
|
||||||
@ -0,0 +1 @@
|
|||||||
|
<% response.redirect('/vods/rss.xml', 301) %>
|
||||||
26
services/pocketbase/pb_hooks/pages/vods/feed.json/+load.js
Normal file
26
services/pocketbase/pb_hooks/pages/vods/feed.json/+load.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// +load.js
|
||||||
|
|
||||||
|
/** @type {import('pocketpages').PageDataLoaderFunc} */
|
||||||
|
module.exports = function (api) {
|
||||||
|
const { params, response } = api;
|
||||||
|
try {
|
||||||
|
const vods = $app.findRecordsByFilter('vods', null, '-streamDate');
|
||||||
|
$app.expandRecords(vods, ["vtubers"], null);
|
||||||
|
// vods.expandedAll("vtubers");
|
||||||
|
return { vods };
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error('error!', e.message);
|
||||||
|
|
||||||
|
if (e.message.match(/no rows/)) {
|
||||||
|
console.log('we are sending 404')
|
||||||
|
return response.html(404, 'VODs not found')
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.log('we are sending error 500')
|
||||||
|
return response.html(500, 'Unknown internal error while fetching vods')
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
29
services/pocketbase/pb_hooks/pages/vods/feed.json/index.ejs
Normal file
29
services/pocketbase/pb_hooks/pages/vods/feed.json/index.ejs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<%#
|
||||||
|
/**
|
||||||
|
* index.ejs - Generates a JSON feed
|
||||||
|
* Expects: data.vods = [{ id, title, url, description, publishedAt, ... }]
|
||||||
|
*/
|
||||||
|
%>
|
||||||
|
|
||||||
|
<%
|
||||||
|
const feed = {
|
||||||
|
version: "https://jsonfeed.org/version/1",
|
||||||
|
title: `${meta('title')} VODs`,
|
||||||
|
home_page_url: "https://futureporn.net",
|
||||||
|
feed_url: "https://futureporn.net/vods/feed.json",
|
||||||
|
description: meta('description'),
|
||||||
|
icon: "https://futureporn.net/images/futureporn-icon.png",
|
||||||
|
author: {
|
||||||
|
name: "CJ_Clippy",
|
||||||
|
url: "https://futureporn.net"
|
||||||
|
},
|
||||||
|
items: data.vods.map(vod => ({
|
||||||
|
content_html: "",
|
||||||
|
url: `https://futureporn.net/vods/${vod.id}`,
|
||||||
|
title: vod.title,
|
||||||
|
summary: vod.notes || vod.title,
|
||||||
|
image: vod.thumbnail,
|
||||||
|
date_modified: vod.updated
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
%><%- JSON.stringify(feed, null, 2) %>
|
||||||
28
services/pocketbase/pb_hooks/pages/vods/feed.xml/+load.js
Normal file
28
services/pocketbase/pb_hooks/pages/vods/feed.xml/+load.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// +load.js
|
||||||
|
|
||||||
|
/** @type {import('pocketpages').PageDataLoaderFunc} */
|
||||||
|
module.exports = function (api) {
|
||||||
|
const { params, response } = api;
|
||||||
|
try {
|
||||||
|
const vods = $app.findRecordsByFilter('vods', null, '-streamDate', 99);
|
||||||
|
$app.expandRecords(vods, ["vtubers"], null);
|
||||||
|
|
||||||
|
|
||||||
|
console.log(JSON.stringify(vods, null, 2))
|
||||||
|
return { vods };
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error('error!', e.message);
|
||||||
|
|
||||||
|
if (e.message.match(/no rows/)) {
|
||||||
|
console.log('we are sending 404')
|
||||||
|
return response.html(404, 'VODs not found')
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.log('we are sending error 500')
|
||||||
|
return response.html(500, 'Unknown internal error while fetching vods')
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
58
services/pocketbase/pb_hooks/pages/vods/feed.xml/index.ejs
Normal file
58
services/pocketbase/pb_hooks/pages/vods/feed.xml/index.ejs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<%#
|
||||||
|
index.ejs — ATOM Feed Generator
|
||||||
|
Expects: data.vods = [{ id, title, notes, thumbnail, streamDate }]
|
||||||
|
%>
|
||||||
|
|
||||||
|
<%=
|
||||||
|
response.header("Content-Type", "application/atom+xml");
|
||||||
|
|
||||||
|
// Build Atom XML as a string
|
||||||
|
let atom = `<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||||
|
<title>Futureporn.net</title>
|
||||||
|
<link href="<%= env('ORIGIN') %>"/>
|
||||||
|
<updated>${new Date().toISOString()}</updated>
|
||||||
|
<id><%= env('ORIGIN') %></id>
|
||||||
|
<author>
|
||||||
|
<name>CJ_Clippy</name>
|
||||||
|
<email>cj@futureporn.net</email>
|
||||||
|
</author>`;
|
||||||
|
|
||||||
|
for (const vod of data.vods) {
|
||||||
|
const url = `${env('ORIGIN')}/vods/${vod.id}`;
|
||||||
|
const entryTitle = ((vod?.get('expand')?.vtubers?.map(vt => vt.get('displayName')) || [])
|
||||||
|
.join(', ') || vod.get('streamDate'));
|
||||||
|
|
||||||
|
atom += `
|
||||||
|
<entry>
|
||||||
|
<title>
|
||||||
|
<![CDATA[${entryTitle}]]>
|
||||||
|
</title>
|
||||||
|
<link href="${url}" />
|
||||||
|
<id>${url}</id>
|
||||||
|
<updated>${vod.get('streamDate')}</updated>`;
|
||||||
|
|
||||||
|
if (vod.get('notes')) {
|
||||||
|
atom += `
|
||||||
|
<summary type="html">
|
||||||
|
<![CDATA[${vod.get('notes')}]]>
|
||||||
|
</summary>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vod.get('thumbnail')) {
|
||||||
|
atom += `
|
||||||
|
<content type="image">
|
||||||
|
<![CDATA[<img src="${vod.get('thumbnail')}" />]]>
|
||||||
|
</content>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
atom += `
|
||||||
|
</entry>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
atom += `
|
||||||
|
</feed>`;
|
||||||
|
|
||||||
|
// Send the Atom feed
|
||||||
|
response.html(200, '<p>HELLO</p>');
|
||||||
|
%>
|
||||||
28
services/pocketbase/pb_hooks/pages/vods/rss.xml/+load.js
Normal file
28
services/pocketbase/pb_hooks/pages/vods/rss.xml/+load.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// +load.js
|
||||||
|
|
||||||
|
/** @type {import('pocketpages').PageDataLoaderFunc} */
|
||||||
|
module.exports = function (api) {
|
||||||
|
const { params, response } = api;
|
||||||
|
try {
|
||||||
|
const vods = $app.findRecordsByFilter('vods', null, '-streamDate', 99);
|
||||||
|
$app.expandRecords(vods, ["vtubers"], null);
|
||||||
|
|
||||||
|
|
||||||
|
console.log(JSON.stringify(vods, null, 2))
|
||||||
|
return { vods };
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error('error!', e.message);
|
||||||
|
|
||||||
|
if (e.message.match(/no rows/)) {
|
||||||
|
console.log('we are sending 404')
|
||||||
|
return response.html(404, 'VODs not found')
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.log('we are sending error 500')
|
||||||
|
return response.html(500, 'Unknown internal error while fetching vods')
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
56
services/pocketbase/pb_hooks/pages/vods/rss.xml/index.ejs
Normal file
56
services/pocketbase/pb_hooks/pages/vods/rss.xml/index.ejs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<%#
|
||||||
|
index.ejs — RSS Feed Generator (RSS 2.0)
|
||||||
|
Expects: data.vods = [{ id, title, notes, thumbnail, streamDate }]
|
||||||
|
%>
|
||||||
|
|
||||||
|
<%
|
||||||
|
response.header("Content-Type", "application/rss+xml")
|
||||||
|
|
||||||
|
// Build RSS XML as a string
|
||||||
|
let rss = `<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
|
||||||
|
<channel>
|
||||||
|
<title>Futureporn.net</title>
|
||||||
|
<link>https://futureporn.net</link>
|
||||||
|
<description>Dedication to the preservaton of lewdtuber history</description>
|
||||||
|
<lastBuildDate>${new Date().toUTCString()}</lastBuildDate>
|
||||||
|
<docs>https://validator.w3.org/feed/docs/rss2.html</docs>
|
||||||
|
<generator> </generator>
|
||||||
|
<language>en</language>
|
||||||
|
<image>
|
||||||
|
<title>Futureporn.net</title>
|
||||||
|
<url>https://futureporn.net/images/futureporn-icon.png</url>
|
||||||
|
<link>https://futureporn.net</link>
|
||||||
|
</image>`;
|
||||||
|
|
||||||
|
for (const vod of data.vods) {
|
||||||
|
const url = `${env('ORIGIN')}/vods/${vod.id}`;
|
||||||
|
rss += `
|
||||||
|
<item>
|
||||||
|
|
||||||
|
<title><![CDATA[${
|
||||||
|
((vod?.get('expand')?.vtubers?.map(vt => vt.get('displayName')) || []).join(', ')
|
||||||
|
|| vod.get('streamDate'))
|
||||||
|
}]]></title>
|
||||||
|
<link>${url}</link>
|
||||||
|
<guid>${url}</guid>
|
||||||
|
<pubDate>${vod.get('streamDate')}</pubDate>`;
|
||||||
|
if (vod.notes) {
|
||||||
|
rss += `
|
||||||
|
<description><![CDATA[${vod.notes}]]></description>`;
|
||||||
|
}
|
||||||
|
if (vod.thumbnail) {
|
||||||
|
rss += `
|
||||||
|
<enclosure url="${vod.thumbnail}" length="0" type="image/png" />`;
|
||||||
|
}
|
||||||
|
rss += `
|
||||||
|
</item>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
rss += `
|
||||||
|
</channel>
|
||||||
|
</rss>`;
|
||||||
|
|
||||||
|
// Send the RSS
|
||||||
|
response.html(200, rss);
|
||||||
|
%>
|
||||||
1
services/pocketbase/pb_hooks/pocketpages.pb.js
Normal file
1
services/pocketbase/pb_hooks/pocketpages.pb.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
require('pocketpages')
|
||||||
79
services/pocketbase/pb_migrations/1761803155_created_vod.js
Normal file
79
services/pocketbase/pb_migrations/1761803155_created_vod.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = new Collection({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "[a-z0-9]{15}",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3208210256",
|
||||||
|
"max": 15,
|
||||||
|
"min": 15,
|
||||||
|
"name": "id",
|
||||||
|
"pattern": "^[a-z0-9]+$",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": true,
|
||||||
|
"required": true,
|
||||||
|
"system": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "date3986823616",
|
||||||
|
"max": "",
|
||||||
|
"min": "",
|
||||||
|
"name": "streamDate",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"exceptDomains": null,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "url3307693472",
|
||||||
|
"name": "announceUrl",
|
||||||
|
"onlyDomains": null,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "url"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "autodate2990389176",
|
||||||
|
"name": "created",
|
||||||
|
"onCreate": true,
|
||||||
|
"onUpdate": false,
|
||||||
|
"presentable": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "autodate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "autodate3332085495",
|
||||||
|
"name": "updated",
|
||||||
|
"onCreate": true,
|
||||||
|
"onUpdate": true,
|
||||||
|
"presentable": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "autodate"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "pbc_144770472",
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": null,
|
||||||
|
"name": "vod",
|
||||||
|
"system": false,
|
||||||
|
"type": "base",
|
||||||
|
"updateRule": null,
|
||||||
|
"viewRule": null
|
||||||
|
});
|
||||||
|
|
||||||
|
return app.save(collection);
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472");
|
||||||
|
|
||||||
|
return app.delete(collection);
|
||||||
|
})
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = new Collection({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "[a-z0-9]{15}",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3208210256",
|
||||||
|
"max": 15,
|
||||||
|
"min": 15,
|
||||||
|
"name": "id",
|
||||||
|
"pattern": "^[a-z0-9]+$",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": true,
|
||||||
|
"required": true,
|
||||||
|
"system": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "autodate2990389176",
|
||||||
|
"name": "created",
|
||||||
|
"onCreate": true,
|
||||||
|
"onUpdate": false,
|
||||||
|
"presentable": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "autodate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "autodate3332085495",
|
||||||
|
"name": "updated",
|
||||||
|
"onCreate": true,
|
||||||
|
"onUpdate": true,
|
||||||
|
"presentable": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "autodate"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "pbc_3872109612",
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": null,
|
||||||
|
"name": "vtuber",
|
||||||
|
"system": false,
|
||||||
|
"type": "base",
|
||||||
|
"updateRule": null,
|
||||||
|
"viewRule": null
|
||||||
|
});
|
||||||
|
|
||||||
|
return app.save(collection);
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3872109612");
|
||||||
|
|
||||||
|
return app.delete(collection);
|
||||||
|
})
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3872109612")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(1, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text1731158936",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "displayName",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(2, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text2560465762",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "slug",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3872109612")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("text1731158936")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("text2560465762")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
352
services/pocketbase/pb_migrations/1761818023_updated_vtuber.js
Normal file
352
services/pocketbase/pb_migrations/1761818023_updated_vtuber.js
Normal file
@ -0,0 +1,352 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3872109612")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(3, new Field({
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_144770472",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation3825607268",
|
||||||
|
"maxSelect": 999,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "vods",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(4, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "file3309110367",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"maxSize": 0,
|
||||||
|
"mimeTypes": [],
|
||||||
|
"name": "image",
|
||||||
|
"presentable": false,
|
||||||
|
"protected": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"thumbs": [],
|
||||||
|
"type": "file"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(5, new Field({
|
||||||
|
"exceptDomains": null,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "url1288386755",
|
||||||
|
"name": "chaturbate",
|
||||||
|
"onlyDomains": null,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "url"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(6, new Field({
|
||||||
|
"exceptDomains": null,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "url376077238",
|
||||||
|
"name": "twitter",
|
||||||
|
"onlyDomains": null,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "url"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(7, new Field({
|
||||||
|
"exceptDomains": null,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "url2042351438",
|
||||||
|
"name": "patreon",
|
||||||
|
"onlyDomains": null,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "url"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(8, new Field({
|
||||||
|
"exceptDomains": null,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "url1928100768",
|
||||||
|
"name": "twitch",
|
||||||
|
"onlyDomains": null,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "url"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(9, new Field({
|
||||||
|
"exceptDomains": null,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "url847873150",
|
||||||
|
"name": "tiktok",
|
||||||
|
"onlyDomains": null,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "url"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(10, new Field({
|
||||||
|
"exceptDomains": null,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "url1505654846",
|
||||||
|
"name": "onlyfans",
|
||||||
|
"onlyDomains": null,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "url"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(11, new Field({
|
||||||
|
"exceptDomains": null,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "url4034435380",
|
||||||
|
"name": "youtube",
|
||||||
|
"onlyDomains": null,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "url"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(12, new Field({
|
||||||
|
"exceptDomains": null,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "url3056519709",
|
||||||
|
"name": "linktree",
|
||||||
|
"onlyDomains": null,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "url"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(13, new Field({
|
||||||
|
"exceptDomains": null,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "url2609473773",
|
||||||
|
"name": "carrd",
|
||||||
|
"onlyDomains": null,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "url"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(14, new Field({
|
||||||
|
"exceptDomains": null,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "url2799872019",
|
||||||
|
"name": "fansly",
|
||||||
|
"onlyDomains": null,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "url"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(15, new Field({
|
||||||
|
"exceptDomains": null,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "url1736283674",
|
||||||
|
"name": "pornhub",
|
||||||
|
"onlyDomains": null,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "url"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(16, new Field({
|
||||||
|
"exceptDomains": null,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "url1322392000",
|
||||||
|
"name": "discord",
|
||||||
|
"onlyDomains": null,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "url"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(17, new Field({
|
||||||
|
"exceptDomains": null,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "url3191313207",
|
||||||
|
"name": "reddit",
|
||||||
|
"onlyDomains": null,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "url"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(18, new Field({
|
||||||
|
"exceptDomains": null,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "url3425232140",
|
||||||
|
"name": "throne",
|
||||||
|
"onlyDomains": null,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "url"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(19, new Field({
|
||||||
|
"exceptDomains": null,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "url2225635011",
|
||||||
|
"name": "instagram",
|
||||||
|
"onlyDomains": null,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "url"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(20, new Field({
|
||||||
|
"exceptDomains": null,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "url1802815712",
|
||||||
|
"name": "facebook",
|
||||||
|
"onlyDomains": null,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "url"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(21, new Field({
|
||||||
|
"exceptDomains": null,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "url4055117536",
|
||||||
|
"name": "merch",
|
||||||
|
"onlyDomains": null,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "url"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(22, new Field({
|
||||||
|
"convertURLs": false,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "editor1843675174",
|
||||||
|
"maxSize": 0,
|
||||||
|
"name": "description",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "editor"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(23, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "number2960516043",
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"name": "themeColor",
|
||||||
|
"onlyInt": false,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "number"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3872109612")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("relation3825607268")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("file3309110367")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("url1288386755")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("url376077238")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("url2042351438")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("url1928100768")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("url847873150")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("url1505654846")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("url4034435380")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("url3056519709")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("url2609473773")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("url2799872019")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("url1736283674")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("url1322392000")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("url3191313207")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("url3425232140")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("url2225635011")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("url1802815712")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("url4055117536")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("editor1843675174")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("number2960516043")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
20
services/pocketbase/pb_migrations/1761818044_updated_vod.js
Normal file
20
services/pocketbase/pb_migrations/1761818044_updated_vod.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"name": "vods"
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"name": "vod"
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3872109612")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"name": "vtubers"
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3872109612")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"name": "vtuber"
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
139
services/pocketbase/pb_migrations/1761818487_updated_vods.js
Normal file
139
services/pocketbase/pb_migrations/1761818487_updated_vods.js
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(3, new Field({
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "_pb_users_auth_",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation1668006755",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "uploader",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(4, new Field({
|
||||||
|
"convertURLs": false,
|
||||||
|
"hidden": false,
|
||||||
|
"id": "editor18589324",
|
||||||
|
"maxSize": 0,
|
||||||
|
"name": "notes",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "editor"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(5, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json2596236420",
|
||||||
|
"maxSize": 0,
|
||||||
|
"name": "segmentKeys",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(6, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "file3785949232",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"maxSize": 0,
|
||||||
|
"mimeTypes": [],
|
||||||
|
"name": "sourceVideo",
|
||||||
|
"presentable": false,
|
||||||
|
"protected": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"thumbs": [],
|
||||||
|
"type": "file"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(7, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "file651090729",
|
||||||
|
"maxSelect": 99,
|
||||||
|
"maxSize": 0,
|
||||||
|
"mimeTypes": [],
|
||||||
|
"name": "segments",
|
||||||
|
"presentable": false,
|
||||||
|
"protected": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"thumbs": [],
|
||||||
|
"type": "file"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(8, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "file3277268710",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"maxSize": 0,
|
||||||
|
"mimeTypes": [],
|
||||||
|
"name": "thumbnail",
|
||||||
|
"presentable": false,
|
||||||
|
"protected": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"thumbs": [],
|
||||||
|
"type": "file"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(9, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "select2063623452",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"name": "status",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "select",
|
||||||
|
"values": [
|
||||||
|
"ordering",
|
||||||
|
"pending",
|
||||||
|
"approved",
|
||||||
|
"rejected",
|
||||||
|
"processing",
|
||||||
|
"processed"
|
||||||
|
]
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("relation1668006755")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("editor18589324")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("json2596236420")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("file3785949232")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("file651090729")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("file3277268710")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("select2063623452")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
28
services/pocketbase/pb_migrations/1761818515_updated_vods.js
Normal file
28
services/pocketbase/pb_migrations/1761818515_updated_vods.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(10, new Field({
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_3872109612",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation3093501878",
|
||||||
|
"maxSelect": 999,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "vtubers",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("relation3093501878")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3872109612")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("number2960516043")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(23, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text2960516043",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "themeColor",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3872109612")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(23, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "number2960516043",
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"name": "themeColor",
|
||||||
|
"onlyInt": false,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "number"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("text2960516043")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
124
services/pocketbase/pb_migrations/1761915053_updated_vods.js
Normal file
124
services/pocketbase/pb_migrations/1761915053_updated_vods.js
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("file3785949232")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("file651090729")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("file3277268710")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(8, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3785949232",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "sourceVideo",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(9, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text651090729",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "segments",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(10, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3277268710",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "thumbnail",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(6, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "file3785949232",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"maxSize": 0,
|
||||||
|
"mimeTypes": [],
|
||||||
|
"name": "sourceVideo",
|
||||||
|
"presentable": false,
|
||||||
|
"protected": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"thumbs": [],
|
||||||
|
"type": "file"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(7, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "file651090729",
|
||||||
|
"maxSelect": 99,
|
||||||
|
"maxSize": 0,
|
||||||
|
"mimeTypes": [],
|
||||||
|
"name": "segments",
|
||||||
|
"presentable": false,
|
||||||
|
"protected": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"thumbs": [],
|
||||||
|
"type": "file"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(8, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "file3277268710",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"maxSize": 0,
|
||||||
|
"mimeTypes": [],
|
||||||
|
"name": "thumbnail",
|
||||||
|
"presentable": false,
|
||||||
|
"protected": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"thumbs": [],
|
||||||
|
"type": "file"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("text3785949232")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("text651090729")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("text3277268710")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
67
services/pocketbase/pb_migrations/1761915570_updated_vods.js
Normal file
67
services/pocketbase/pb_migrations/1761915570_updated_vods.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(11, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3889096196",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "muxAssetId",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(12, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text1061856600",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "muxPlaybackId",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(13, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text1805914144",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "ipfsCid",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("text3889096196")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("text1061856600")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("text1805914144")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
29
services/pocketbase/pb_migrations/1761925981_updated_vods.js
Normal file
29
services/pocketbase/pb_migrations/1761925981_updated_vods.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(14, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text1646005928",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "videoSrcB2",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("text1646005928")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
29
services/pocketbase/pb_migrations/1762063470_updated_vods.js
Normal file
29
services/pocketbase/pb_migrations/1762063470_updated_vods.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(15, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text1642358089",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "muxPlaybackToken",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("text1642358089")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"oauth2": {
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"oauth2": {
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(8, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "select66088533",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"name": "supporter",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "select",
|
||||||
|
"values": [
|
||||||
|
"free",
|
||||||
|
"tier1",
|
||||||
|
"tier2",
|
||||||
|
"tier3",
|
||||||
|
"tier4",
|
||||||
|
"tier5",
|
||||||
|
"tier6"
|
||||||
|
]
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("select66088533")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("select66088533")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(8, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "bool66088533",
|
||||||
|
"name": "supporter",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "bool"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(8, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "select66088533",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"name": "supporter",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "select",
|
||||||
|
"values": [
|
||||||
|
"free",
|
||||||
|
"tier1",
|
||||||
|
"tier2",
|
||||||
|
"tier3",
|
||||||
|
"tier4",
|
||||||
|
"tier5",
|
||||||
|
"tier6"
|
||||||
|
]
|
||||||
|
}))
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("bool66088533")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("bool66088533")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(8, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "bool3858055773",
|
||||||
|
"name": "patron",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "bool"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(8, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "bool66088533",
|
||||||
|
"name": "supporter",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "bool"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("bool3858055773")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(9, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text1335658467",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "patreonId",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("text1335658467")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_4199263681");
|
||||||
|
|
||||||
|
return app.delete(collection);
|
||||||
|
}, (app) => {
|
||||||
|
const collection = new Collection({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3208210256",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "id",
|
||||||
|
"pattern": "^[a-z0-9]+$",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": true,
|
||||||
|
"required": true,
|
||||||
|
"system": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json3486377887",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "pending_tasks",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json907408884",
|
||||||
|
"maxSize": 1,
|
||||||
|
"name": "failed_tasks",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "pbc_4199263681",
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": null,
|
||||||
|
"name": "queue_stats",
|
||||||
|
"system": false,
|
||||||
|
"type": "view",
|
||||||
|
"updateRule": null,
|
||||||
|
"viewQuery": "SELECT queue as id,\n SUM(CASE WHEN failed IS '' THEN 1 ELSE 0 END) as pending_tasks,\n SUM(CASE WHEN failed IS NOT '' THEN 1 ELSE 0 END) as failed_tasks\n FROM queue_tasks\n GROUP BY queue;",
|
||||||
|
"viewRule": null
|
||||||
|
});
|
||||||
|
|
||||||
|
return app.save(collection);
|
||||||
|
})
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_1488669058");
|
||||||
|
|
||||||
|
return app.delete(collection);
|
||||||
|
}, (app) => {
|
||||||
|
const collection = new Collection({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "[a-z0-9]{15}",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3208210256",
|
||||||
|
"max": 15,
|
||||||
|
"min": 15,
|
||||||
|
"name": "id",
|
||||||
|
"pattern": "^[a-z0-9]+$",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": true,
|
||||||
|
"required": true,
|
||||||
|
"system": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text1797306934",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "worker_id",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_4107420034",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation1384045349",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "task",
|
||||||
|
"presentable": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "autodate2990389176",
|
||||||
|
"name": "created",
|
||||||
|
"onCreate": true,
|
||||||
|
"onUpdate": false,
|
||||||
|
"presentable": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "autodate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "autodate3332085495",
|
||||||
|
"name": "updated",
|
||||||
|
"onCreate": true,
|
||||||
|
"onUpdate": true,
|
||||||
|
"presentable": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "autodate"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "pbc_1488669058",
|
||||||
|
"indexes": [
|
||||||
|
"CREATE UNIQUE INDEX idx_HSFtKfUArr ON queue_locks (task)",
|
||||||
|
"CREATE INDEX idx_bn5iw0BzOm ON queue_locks (created)"
|
||||||
|
],
|
||||||
|
"listRule": null,
|
||||||
|
"name": "queue_locks",
|
||||||
|
"system": false,
|
||||||
|
"type": "base",
|
||||||
|
"updateRule": null,
|
||||||
|
"viewRule": null
|
||||||
|
});
|
||||||
|
|
||||||
|
return app.save(collection);
|
||||||
|
})
|
||||||
@ -0,0 +1,109 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_4107420034");
|
||||||
|
|
||||||
|
return app.delete(collection);
|
||||||
|
}, (app) => {
|
||||||
|
const collection = new Collection({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "[a-z0-9]{15}",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3208210256",
|
||||||
|
"max": 15,
|
||||||
|
"min": 15,
|
||||||
|
"name": "id",
|
||||||
|
"pattern": "^[a-z0-9]+$",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": true,
|
||||||
|
"required": true,
|
||||||
|
"system": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text2147319651",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "queue",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": true,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "json1384045349",
|
||||||
|
"maxSize": 2000000,
|
||||||
|
"name": "task",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "date2659951479",
|
||||||
|
"max": "",
|
||||||
|
"min": "",
|
||||||
|
"name": "failed",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text4016436606",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "failed_reason",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "autodate2990389176",
|
||||||
|
"name": "created",
|
||||||
|
"onCreate": true,
|
||||||
|
"onUpdate": false,
|
||||||
|
"presentable": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "autodate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "autodate3332085495",
|
||||||
|
"name": "updated",
|
||||||
|
"onCreate": true,
|
||||||
|
"onUpdate": true,
|
||||||
|
"presentable": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "autodate"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "pbc_4107420034",
|
||||||
|
"indexes": [
|
||||||
|
"CREATE INDEX idx_DjQFazUd5h ON queue_tasks (\n queue,\n failed,\n created\n)",
|
||||||
|
"CREATE INDEX idx_pUcaFfpCFF ON queue_tasks (\n failed,\n updated\n)"
|
||||||
|
],
|
||||||
|
"listRule": null,
|
||||||
|
"name": "queue_tasks",
|
||||||
|
"system": false,
|
||||||
|
"type": "base",
|
||||||
|
"updateRule": null,
|
||||||
|
"viewRule": null
|
||||||
|
});
|
||||||
|
|
||||||
|
return app.save(collection);
|
||||||
|
})
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"oauth2": {
|
||||||
|
"mappedFields": {
|
||||||
|
"avatarURL": "",
|
||||||
|
"id": "patreonId"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"oauth2": {
|
||||||
|
"mappedFields": {
|
||||||
|
"avatarURL": "avatar",
|
||||||
|
"id": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(10, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3341984971",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "patreonAccessToken",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("text3341984971")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3872109612")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("relation3825607268")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3872109612")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(3, new Field({
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"collectionId": "pbc_144770472",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "relation3825607268",
|
||||||
|
"maxSelect": 999,
|
||||||
|
"minSelect": 0,
|
||||||
|
"name": "vods",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "relation"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
48
services/pocketbase/pb_migrations/1762500151_updated_vods.js
Normal file
48
services/pocketbase/pb_migrations/1762500151_updated_vods.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("text3277268710")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(15, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "file3277268710",
|
||||||
|
"maxSelect": 1,
|
||||||
|
"maxSize": 0,
|
||||||
|
"mimeTypes": [],
|
||||||
|
"name": "thumbnail",
|
||||||
|
"presentable": false,
|
||||||
|
"protected": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"thumbs": [],
|
||||||
|
"type": "file"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(10, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3277268710",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "thumbnail",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("file3277268710")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = new Collection({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3208210256",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "id",
|
||||||
|
"pattern": "^[a-z0-9]+$",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": true,
|
||||||
|
"required": true,
|
||||||
|
"system": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_oC8t",
|
||||||
|
"max": "",
|
||||||
|
"min": "",
|
||||||
|
"name": "streamDate",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "date"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "pbc_3009055234",
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": null,
|
||||||
|
"name": "vtuber_vods",
|
||||||
|
"system": false,
|
||||||
|
"type": "view",
|
||||||
|
"updateRule": null,
|
||||||
|
"viewQuery": "SELECT\n vods.id,\n vods.streamDate\nFROM vods\nLEFT JOIN json_each(vods.vtubers) ON json_each.value = 'el_xox'",
|
||||||
|
"viewRule": null
|
||||||
|
});
|
||||||
|
|
||||||
|
return app.save(collection);
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3009055234");
|
||||||
|
|
||||||
|
return app.delete(collection);
|
||||||
|
})
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3009055234")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"viewQuery": "SELECT\n vods.id,\n vods.streamDate\nFROM vods\nLEFT JOIN json_each(vods.vtubers) ON json_each.value = 'udqmxs649ajf2mk'"
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_oC8t")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(1, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_1BVl",
|
||||||
|
"max": "",
|
||||||
|
"min": "",
|
||||||
|
"name": "streamDate",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "date"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3009055234")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"viewQuery": "SELECT\n vods.id,\n vods.streamDate\nFROM vods\nLEFT JOIN json_each(vods.vtubers) ON json_each.value = 'el_xox'"
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(1, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_oC8t",
|
||||||
|
"max": "",
|
||||||
|
"min": "",
|
||||||
|
"name": "streamDate",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "date"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_1BVl")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3009055234")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"viewQuery": "SELECT\n vods.id,\n vods.streamDate,\n vtubers.displayName\nFROM vods\nLEFT JOIN vtubers"
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_1BVl")
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(1, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_h4Aw",
|
||||||
|
"max": "",
|
||||||
|
"min": "",
|
||||||
|
"name": "streamDate",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "date"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(2, new Field({
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_yNn2",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "displayName",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}))
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3009055234")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"viewQuery": "SELECT\n vods.id,\n vods.streamDate\nFROM vods\nLEFT JOIN json_each(vods.vtubers) ON json_each.value = 'udqmxs649ajf2mk'"
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
// add field
|
||||||
|
collection.fields.addAt(1, new Field({
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_1BVl",
|
||||||
|
"max": "",
|
||||||
|
"min": "",
|
||||||
|
"name": "streamDate",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "date"
|
||||||
|
}))
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_h4Aw")
|
||||||
|
|
||||||
|
// remove field
|
||||||
|
collection.fields.removeById("_clone_yNn2")
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3009055234");
|
||||||
|
|
||||||
|
return app.delete(collection);
|
||||||
|
}, (app) => {
|
||||||
|
const collection = new Collection({
|
||||||
|
"createRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "text3208210256",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "id",
|
||||||
|
"pattern": "^[a-z0-9]+$",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": true,
|
||||||
|
"required": true,
|
||||||
|
"system": true,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_h4Aw",
|
||||||
|
"max": "",
|
||||||
|
"min": "",
|
||||||
|
"name": "streamDate",
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autogeneratePattern": "",
|
||||||
|
"hidden": false,
|
||||||
|
"id": "_clone_yNn2",
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"name": "displayName",
|
||||||
|
"pattern": "",
|
||||||
|
"presentable": false,
|
||||||
|
"primaryKey": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "pbc_3009055234",
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": null,
|
||||||
|
"name": "vtuber_vods",
|
||||||
|
"system": false,
|
||||||
|
"type": "view",
|
||||||
|
"updateRule": null,
|
||||||
|
"viewQuery": "SELECT\n vods.id,\n vods.streamDate,\n vtubers.displayName\nFROM vods\nLEFT JOIN vtubers",
|
||||||
|
"viewRule": null
|
||||||
|
});
|
||||||
|
|
||||||
|
return app.save(collection);
|
||||||
|
})
|
||||||
20
services/pocketbase/pb_migrations/1762622017_updated_vods.js
Normal file
20
services/pocketbase/pb_migrations/1762622017_updated_vods.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"viewRule": ""
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"viewRule": null
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
20
services/pocketbase/pb_migrations/1762622042_updated_vods.js
Normal file
20
services/pocketbase/pb_migrations/1762622042_updated_vods.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"listRule": ""
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"listRule": null
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
22
services/pocketbase/pb_migrations/1762622148_updated_vods.js
Normal file
22
services/pocketbase/pb_migrations/1762622148_updated_vods.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"listRule": null,
|
||||||
|
"viewRule": null
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"listRule": "",
|
||||||
|
"viewRule": ""
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
22
services/pocketbase/pb_migrations/1762622255_updated_vods.js
Normal file
22
services/pocketbase/pb_migrations/1762622255_updated_vods.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"listRule": "",
|
||||||
|
"viewRule": ""
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_144770472")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"listRule": null,
|
||||||
|
"viewRule": null
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3872109612")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"listRule": "",
|
||||||
|
"viewRule": ""
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
}, (app) => {
|
||||||
|
const collection = app.findCollectionByNameOrId("pbc_3872109612")
|
||||||
|
|
||||||
|
// update collection data
|
||||||
|
unmarshal({
|
||||||
|
"listRule": null,
|
||||||
|
"viewRule": null
|
||||||
|
}, collection)
|
||||||
|
|
||||||
|
return app.save(collection)
|
||||||
|
})
|
||||||
BIN
services/pocketbase/pb_public/favicon.ico
Normal file
BIN
services/pocketbase/pb_public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 323 B |
1
services/pocketbase/pb_public/service-worker.js
Normal file
1
services/pocketbase/pb_public/service-worker.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
// hello
|
||||||
957
services/pocketbase/src/plugins/patreon/package-lock.json
generated
Normal file
957
services/pocketbase/src/plugins/patreon/package-lock.json
generated
Normal file
@ -0,0 +1,957 @@
|
|||||||
|
{
|
||||||
|
"name": "pocketpages-plugin-patreon",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "pocketpages-plugin-patreon",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"license": "Unlicense",
|
||||||
|
"dependencies": {
|
||||||
|
"pocketpages-plugin-js-sdk": "0.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"tsdown": "^0.15.12",
|
||||||
|
"typescript": "^5.7.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/generator": {
|
||||||
|
"version": "7.28.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
|
||||||
|
"integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/parser": "^7.28.5",
|
||||||
|
"@babel/types": "^7.28.5",
|
||||||
|
"@jridgewell/gen-mapping": "^0.3.12",
|
||||||
|
"@jridgewell/trace-mapping": "^0.3.28",
|
||||||
|
"jsesc": "^3.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-string-parser": {
|
||||||
|
"version": "7.27.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||||
|
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-validator-identifier": {
|
||||||
|
"version": "7.28.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
||||||
|
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/parser": {
|
||||||
|
"version": "7.28.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
|
||||||
|
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/types": "^7.28.5"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"parser": "bin/babel-parser.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/types": {
|
||||||
|
"version": "7.28.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
|
||||||
|
"integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-string-parser": "^7.27.1",
|
||||||
|
"@babel/helper-validator-identifier": "^7.28.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@emnapi/core": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@emnapi/wasi-threads": "1.1.0",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@emnapi/runtime": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@emnapi/wasi-threads": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/gen-mapping": {
|
||||||
|
"version": "0.3.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||||
|
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||||
|
"@jridgewell/trace-mapping": "^0.3.24"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/resolve-uri": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
|
"version": "1.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||||
|
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
|
"version": "0.3.31",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
|
||||||
|
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/resolve-uri": "^3.1.0",
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@napi-rs/wasm-runtime": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@emnapi/core": "^1.5.0",
|
||||||
|
"@emnapi/runtime": "^1.5.0",
|
||||||
|
"@tybys/wasm-util": "^0.10.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@oxc-project/types": {
|
||||||
|
"version": "0.95.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.95.0.tgz",
|
||||||
|
"integrity": "sha512-vACy7vhpMPhjEJhULNxrdR0D943TkA/MigMpJCHmBHvMXxRStRi/dPtTlfQ3uDwWSzRpT8z+7ImjZVf8JWBocQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/Boshen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@quansync/fs": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@quansync/fs/-/fs-0.1.5.tgz",
|
||||||
|
"integrity": "sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"quansync": "^0.2.11"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sxzz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-android-arm64": {
|
||||||
|
"version": "1.0.0-beta.45",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.45.tgz",
|
||||||
|
"integrity": "sha512-bfgKYhFiXJALeA/riil908+2vlyWGdwa7Ju5S+JgWZYdR4jtiPOGdM6WLfso1dojCh+4ZWeiTwPeV9IKQEX+4g==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-darwin-arm64": {
|
||||||
|
"version": "1.0.0-beta.45",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-beta.45.tgz",
|
||||||
|
"integrity": "sha512-xjCv4CRVsSnnIxTuyH1RDJl5OEQ1c9JYOwfDAHddjJDxCw46ZX9q80+xq7Eok7KC4bRSZudMJllkvOKv0T9SeA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-darwin-x64": {
|
||||||
|
"version": "1.0.0-beta.45",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-beta.45.tgz",
|
||||||
|
"integrity": "sha512-ddcO9TD3D/CLUa/l8GO8LHzBOaZqWg5ClMy3jICoxwCuoz47h9dtqPsIeTiB6yR501LQTeDsjA4lIFd7u3Ljfw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-freebsd-x64": {
|
||||||
|
"version": "1.0.0-beta.45",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-beta.45.tgz",
|
||||||
|
"integrity": "sha512-MBTWdrzW9w+UMYDUvnEuh0pQvLENkl2Sis15fHTfHVW7ClbGuez+RWopZudIDEGkpZXdeI4CkRXk+vdIIebrmg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
||||||
|
"version": "1.0.0-beta.45",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-beta.45.tgz",
|
||||||
|
"integrity": "sha512-4YgoCFiki1HR6oSg+GxxfzfnVCesQxLF1LEnw9uXS/MpBmuog0EOO2rYfy69rWP4tFZL9IWp6KEfGZLrZ7aUog==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
||||||
|
"version": "1.0.0-beta.45",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-beta.45.tgz",
|
||||||
|
"integrity": "sha512-LE1gjAwQRrbCOorJJ7LFr10s5vqYf5a00V5Ea9wXcT2+56n5YosJkcp8eQ12FxRBv2YX8dsdQJb+ZTtYJwb6XQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
||||||
|
"version": "1.0.0-beta.45",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-beta.45.tgz",
|
||||||
|
"integrity": "sha512-tdy8ThO/fPp40B81v0YK3QC+KODOmzJzSUOO37DinQxzlTJ026gqUSOM8tzlVixRbQJltgVDCTYF8HNPRErQTA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
||||||
|
"version": "1.0.0-beta.45",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-beta.45.tgz",
|
||||||
|
"integrity": "sha512-lS082ROBWdmOyVY/0YB3JmsiClaWoxvC+dA8/rbhyB9VLkvVEaihLEOr4CYmrMse151C4+S6hCw6oa1iewox7g==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-linux-x64-musl": {
|
||||||
|
"version": "1.0.0-beta.45",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-beta.45.tgz",
|
||||||
|
"integrity": "sha512-Hi73aYY0cBkr1/SvNQqH8Cd+rSV6S9RB5izCv0ySBcRnd/Wfn5plguUoGYwBnhHgFbh6cPw9m2dUVBR6BG1gxA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-openharmony-arm64": {
|
||||||
|
"version": "1.0.0-beta.45",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-beta.45.tgz",
|
||||||
|
"integrity": "sha512-fljEqbO7RHHogNDxYtTzr+GNjlfOx21RUyGmF+NrkebZ8emYYiIqzPxsaMZuRx0rgZmVmliOzEp86/CQFDKhJQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openharmony"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-wasm32-wasi": {
|
||||||
|
"version": "1.0.0-beta.45",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-beta.45.tgz",
|
||||||
|
"integrity": "sha512-ZJDB7lkuZE9XUnWQSYrBObZxczut+8FZ5pdanm8nNS1DAo8zsrPuvGwn+U3fwU98WaiFsNrA4XHngesCGr8tEQ==",
|
||||||
|
"cpu": [
|
||||||
|
"wasm32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@napi-rs/wasm-runtime": "^1.0.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||||
|
"version": "1.0.0-beta.45",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-beta.45.tgz",
|
||||||
|
"integrity": "sha512-zyzAjItHPUmxg6Z8SyRhLdXlJn3/D9KL5b9mObUrBHhWS/GwRH4665xCiFqeuktAhhWutqfc+rOV2LjK4VYQGQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-win32-ia32-msvc": {
|
||||||
|
"version": "1.0.0-beta.45",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.0.0-beta.45.tgz",
|
||||||
|
"integrity": "sha512-wODcGzlfxqS6D7BR0srkJk3drPwXYLu7jPHN27ce2c4PUnVVmJnp9mJzUQGT4LpmHmmVdMZ+P6hKvyTGBzc1CA==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
||||||
|
"version": "1.0.0-beta.45",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-beta.45.tgz",
|
||||||
|
"integrity": "sha512-wiU40G1nQo9rtfvF9jLbl79lUgjfaD/LTyUEw2Wg/gdF5OhjzpKMVugZQngO+RNdwYaNj+Fs+kWBWfp4VXPMHA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/pluginutils": {
|
||||||
|
"version": "1.0.0-beta.45",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.45.tgz",
|
||||||
|
"integrity": "sha512-Le9ulGCrD8ggInzWw/k2J8QcbPz7eGIOWqfJ2L+1R0Opm7n6J37s2hiDWlh6LJN0Lk9L5sUzMvRHKW7UxBZsQA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@tybys/wasm-util": {
|
||||||
|
"version": "0.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
||||||
|
"integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ansis": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ast-kit": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-TH+b3Lv6pUjy/Nu0m6A2JULtdzLpmqF9x1Dhj00ZoEiML8qvVA9j1flkzTKNYgdEhWrjDwtWNpyyCUbfQe514g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/parser": "^7.28.4",
|
||||||
|
"pathe": "^2.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sxzz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/birpc": {
|
||||||
|
"version": "2.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/birpc/-/birpc-2.6.1.tgz",
|
||||||
|
"integrity": "sha512-LPnFhlDpdSH6FJhJyn4M0kFO7vtQ5iPw24FnG0y21q09xC7e8+1LeR31S1MAIrDAHp4m7aas4bEkTDTvMAtebQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cac": {
|
||||||
|
"version": "6.7.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
|
||||||
|
"integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chokidar": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"readdirp": "^4.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.16.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/debug": {
|
||||||
|
"version": "4.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||||
|
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "^2.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/defu": {
|
||||||
|
"version": "6.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz",
|
||||||
|
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/diff": {
|
||||||
|
"version": "8.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz",
|
||||||
|
"integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dts-resolver": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/dts-resolver/-/dts-resolver-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-xeXHBQkn2ISSXxbJWD828PFjtyg+/UrMDo7W4Ffcs7+YWCquxU8YjV1KoxuiL+eJ5pg3ll+bC6flVv61L3LKZg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.18.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sxzz"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"oxc-resolver": ">=11.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"oxc-resolver": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/empathic": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fdir": {
|
||||||
|
"version": "6.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||||
|
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"picomatch": "^3 || ^4"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"picomatch": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-tsconfig": {
|
||||||
|
"version": "4.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
|
||||||
|
"integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"resolve-pkg-maps": "^1.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/hookable": {
|
||||||
|
"version": "5.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
|
||||||
|
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/jiti": {
|
||||||
|
"version": "2.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
|
||||||
|
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"jiti": "lib/jiti-cli.mjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jsesc": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"jsesc": "bin/jsesc"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/magic-string": {
|
||||||
|
"version": "0.30.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||||
|
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ms": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/pathe": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/picomatch": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pocketbase-js-sdk-jsvm": {
|
||||||
|
"version": "0.25.10004",
|
||||||
|
"resolved": "https://registry.npmjs.org/pocketbase-js-sdk-jsvm/-/pocketbase-js-sdk-jsvm-0.25.10004.tgz",
|
||||||
|
"integrity": "sha512-/0RkFa6X4LeKMdv4KCXhC5i2cvpG0elvXInRyk8bApjG8jyyPiNDL6o9VBvgxjl6j8/HcQYf3oKYXmZteqAFvQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/pocketpages-plugin-js-sdk": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pocketpages-plugin-js-sdk/-/pocketpages-plugin-js-sdk-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-/uY1nCz7Zt19C/kyoOraH5ekTZa4PLPpHd0qIEoyf1XBPtEpHL4k7jZE0IPEMu1dH/8RfsKb/YzN52GR+9DdzQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"pocketbase-js-sdk-jsvm": "^0.25.10004"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/quansync": {
|
||||||
|
"version": "0.2.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz",
|
||||||
|
"integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/sxzz"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/readdirp": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.18.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/resolve-pkg-maps": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rolldown": {
|
||||||
|
"version": "1.0.0-beta.45",
|
||||||
|
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.45.tgz",
|
||||||
|
"integrity": "sha512-iMmuD72XXLf26Tqrv1cryNYLX6NNPLhZ3AmNkSf8+xda0H+yijjGJ+wVT9UdBUHOpKzq9RjKtQKRCWoEKQQBZQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@oxc-project/types": "=0.95.0",
|
||||||
|
"@rolldown/pluginutils": "1.0.0-beta.45"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rolldown": "bin/cli.mjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@rolldown/binding-android-arm64": "1.0.0-beta.45",
|
||||||
|
"@rolldown/binding-darwin-arm64": "1.0.0-beta.45",
|
||||||
|
"@rolldown/binding-darwin-x64": "1.0.0-beta.45",
|
||||||
|
"@rolldown/binding-freebsd-x64": "1.0.0-beta.45",
|
||||||
|
"@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.45",
|
||||||
|
"@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.45",
|
||||||
|
"@rolldown/binding-linux-arm64-musl": "1.0.0-beta.45",
|
||||||
|
"@rolldown/binding-linux-x64-gnu": "1.0.0-beta.45",
|
||||||
|
"@rolldown/binding-linux-x64-musl": "1.0.0-beta.45",
|
||||||
|
"@rolldown/binding-openharmony-arm64": "1.0.0-beta.45",
|
||||||
|
"@rolldown/binding-wasm32-wasi": "1.0.0-beta.45",
|
||||||
|
"@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.45",
|
||||||
|
"@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.45",
|
||||||
|
"@rolldown/binding-win32-x64-msvc": "1.0.0-beta.45"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rolldown-plugin-dts": {
|
||||||
|
"version": "0.17.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/rolldown-plugin-dts/-/rolldown-plugin-dts-0.17.3.tgz",
|
||||||
|
"integrity": "sha512-8mGnNUVNrqEdTnrlcaDxs4sAZg0No6njO+FuhQd4L56nUbJO1tHxOoKDH3mmMJg7f/BhEj/1KjU5W9kZ9zM/kQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/generator": "^7.28.5",
|
||||||
|
"@babel/parser": "^7.28.5",
|
||||||
|
"@babel/types": "^7.28.5",
|
||||||
|
"ast-kit": "^2.1.3",
|
||||||
|
"birpc": "^2.6.1",
|
||||||
|
"debug": "^4.4.3",
|
||||||
|
"dts-resolver": "^2.1.2",
|
||||||
|
"get-tsconfig": "^4.13.0",
|
||||||
|
"magic-string": "^0.30.21"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.18.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sxzz"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@ts-macro/tsc": "^0.3.6",
|
||||||
|
"@typescript/native-preview": ">=7.0.0-dev.20250601.1",
|
||||||
|
"rolldown": "^1.0.0-beta.44",
|
||||||
|
"typescript": "^5.0.0",
|
||||||
|
"vue-tsc": "~3.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@ts-macro/tsc": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@typescript/native-preview": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"vue-tsc": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/semver": {
|
||||||
|
"version": "7.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||||
|
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tinyexec": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/tinyglobby": {
|
||||||
|
"version": "0.2.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
||||||
|
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fdir": "^6.5.0",
|
||||||
|
"picomatch": "^4.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tree-kill": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"tree-kill": "cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsdown": {
|
||||||
|
"version": "0.15.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/tsdown/-/tsdown-0.15.12.tgz",
|
||||||
|
"integrity": "sha512-c8VLlQm8/lFrOAg5VMVeN4NAbejZyVQkzd+ErjuaQgJFI/9MhR9ivr0H/CM7UlOF1+ELlF6YaI7sU/4itgGQ8w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansis": "^4.2.0",
|
||||||
|
"cac": "^6.7.14",
|
||||||
|
"chokidar": "^4.0.3",
|
||||||
|
"debug": "^4.4.3",
|
||||||
|
"diff": "^8.0.2",
|
||||||
|
"empathic": "^2.0.0",
|
||||||
|
"hookable": "^5.5.3",
|
||||||
|
"rolldown": "1.0.0-beta.45",
|
||||||
|
"rolldown-plugin-dts": "^0.17.2",
|
||||||
|
"semver": "^7.7.3",
|
||||||
|
"tinyexec": "^1.0.1",
|
||||||
|
"tinyglobby": "^0.2.15",
|
||||||
|
"tree-kill": "^1.2.2",
|
||||||
|
"unconfig": "^7.3.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"tsdown": "dist/run.mjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sxzz"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@arethetypeswrong/core": "^0.18.1",
|
||||||
|
"publint": "^0.3.0",
|
||||||
|
"typescript": "^5.0.0",
|
||||||
|
"unplugin-lightningcss": "^0.4.0",
|
||||||
|
"unplugin-unused": "^0.5.0",
|
||||||
|
"unrun": "^0.2.1"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@arethetypeswrong/core": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"publint": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"unplugin-lightningcss": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"unplugin-unused": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"unrun": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "0BSD",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "5.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/unconfig": {
|
||||||
|
"version": "7.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/unconfig/-/unconfig-7.3.3.tgz",
|
||||||
|
"integrity": "sha512-QCkQoOnJF8L107gxfHL0uavn7WD9b3dpBcFX6HtfQYmjw2YzWxGuFQ0N0J6tE9oguCBJn9KOvfqYDCMPHIZrBA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@quansync/fs": "^0.1.5",
|
||||||
|
"defu": "^6.1.4",
|
||||||
|
"jiti": "^2.5.1",
|
||||||
|
"quansync": "^0.2.11"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
services/pocketbase/src/plugins/patreon/package.json
Normal file
19
services/pocketbase/src/plugins/patreon/package.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "pocketpages-plugin-patreon",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"build": "tsdown",
|
||||||
|
"dev": "tsdown --watch"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"pocketpages-plugin-js-sdk": "0.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"tsdown": "^0.15.12",
|
||||||
|
"typescript": "^5.7.3"
|
||||||
|
},
|
||||||
|
"license": "Unlicense"
|
||||||
|
}
|
||||||
40
services/pocketbase/src/plugins/patreon/src/index.ts
Normal file
40
services/pocketbase/src/plugins/patreon/src/index.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { type ExtendContextApiContext } from 'pocketpages'
|
||||||
|
import PocketBase, {
|
||||||
|
AuthModel,
|
||||||
|
RecordAuthResponse,
|
||||||
|
} from 'pocketbase-js-sdk-jsvm'
|
||||||
|
import { type OAuth2SignInOptions } from 'pocketpages-plugin-auth'
|
||||||
|
import { PluginFactory } from 'pocketpages'
|
||||||
|
import { OAuth2ProviderInfo } from 'pocketpages-plugin-auth'
|
||||||
|
|
||||||
|
const authPluginFactory: PluginFactory = (config) => {
|
||||||
|
return {
|
||||||
|
name: 'patreon',
|
||||||
|
onExtendContextApi(context: ExtendContextApiContext) {
|
||||||
|
const { api } = context;
|
||||||
|
const pb = () => api.pb() as PocketBase
|
||||||
|
|
||||||
|
|
||||||
|
api.savePatreonData = (
|
||||||
|
authData: AuthModel
|
||||||
|
) => {
|
||||||
|
pb.collection('users')
|
||||||
|
},
|
||||||
|
|
||||||
|
api.getPatreonUser = (
|
||||||
|
authData: AuthModel,
|
||||||
|
) => {
|
||||||
|
|
||||||
|
|
||||||
|
console.log("AYO we got authData as follo");
|
||||||
|
console.log(JSON.stringify(authData, null, 2));
|
||||||
|
|
||||||
|
// make a request to Patreon
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default authPluginFactory;
|
||||||
19
services/pocketbase/src/plugins/patreon/tsconfig.json
Normal file
19
services/pocketbase/src/plugins/patreon/tsconfig.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": [
|
||||||
|
"pocketbase-jsvm"
|
||||||
|
],
|
||||||
|
"strict": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"target": "ES6",
|
||||||
|
"module": "CommonJS",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"resolveJsonModule": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
10
services/pocketbase/src/plugins/patreon/tsdown.config.ts
Normal file
10
services/pocketbase/src/plugins/patreon/tsdown.config.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { defineConfig } from 'tsdown'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
entry: {
|
||||||
|
index: 'src/index.ts',
|
||||||
|
},
|
||||||
|
format: ['cjs'],
|
||||||
|
clean: true,
|
||||||
|
outDir: 'dist',
|
||||||
|
})
|
||||||
14
services/pocketbase/systemd/pocketbase.service
Normal file
14
services/pocketbase/systemd/pocketbase.service
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[Service]
|
||||||
|
Type = simple
|
||||||
|
User = pb
|
||||||
|
Group = pb
|
||||||
|
LimitNOFILE = 4096
|
||||||
|
Restart = always
|
||||||
|
RestartSec = 5s
|
||||||
|
StandardOutput = append:/home/pb/pb/std.log
|
||||||
|
StandardError = append:/home/pb/pb/std.log
|
||||||
|
WorkingDirectory = /home/pb/pb
|
||||||
|
ExecStart = /home/pb/pb/pocketbase serve
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy = multi-user.target
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This script must run from project root
|
||||||
|
|
||||||
|
|
||||||
|
npx @dotenvx/dotenvx run -f ./.env.development.local -- node ./utils/data_migrations/2025-10-30-v1-export.js ./utils/data_migrations
|
||||||
|
pocketbase import ./utils/data_migrations/vtubers.json vtubers
|
||||||
|
pocketbase import ./utils/data_migrations/vods.json vods
|
||||||
@ -0,0 +1,172 @@
|
|||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migration Script: V1 → json
|
||||||
|
* -----------------------------------
|
||||||
|
* This script migrates VTuber and VOD data from an old Postgres database (V1) into JSON files
|
||||||
|
* One JSON file per collection, suitable for importing using pocketbase import script
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* - Ensure environment variables are configured for both databases:
|
||||||
|
* V1_DB_HOST Hostname of the V1 database (default: "localhost")
|
||||||
|
* V1_DB_PORT Port of the V1 database (default: "5444")
|
||||||
|
* V1_DB_USER Username for the V1 database (default: "postgres")
|
||||||
|
* V1_DB_PASS Password for the V1 database (default: "password")
|
||||||
|
* V1_DB_NAME Database name for V1 (default: "restoredb")
|
||||||
|
* DEFAULT_UPLOADER_ID
|
||||||
|
* An existing user ID in the V2 database that will be set as the uploader for all migrated records.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* Run with Node.js:
|
||||||
|
* $ npx @dotenvx/dotenvx run -f ./.env.local -- node ./2025-10-30-v1-export.js ../pb_data/
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import pg from 'pg';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
const dataDir = process.argv[2]; // e.g., "../../pb_data/"
|
||||||
|
if (!dataDir) {
|
||||||
|
console.error("Please provide the output data directory as the first argument.");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const v1 = new pg.Pool({
|
||||||
|
host: process.env.V1_DB_HOST || 'localhost',
|
||||||
|
port: +(process.env.V1_DB_PORT || '5444'),
|
||||||
|
user: process.env.V1_DB_USER || 'postgres',
|
||||||
|
password: process.env.V1_DB_PASS || 'password',
|
||||||
|
database: process.env.V1_DB_NAME || 'restoredb'
|
||||||
|
});
|
||||||
|
|
||||||
|
async function migrateVtubers() {
|
||||||
|
console.log('Migrating vtubers...');
|
||||||
|
const res = await v1.query(`SELECT * FROM vtubers`);
|
||||||
|
const vtubers = res.rows.map(vt => ({
|
||||||
|
slug: vt.slug,
|
||||||
|
displayName: vt.display_name,
|
||||||
|
chaturbate: vt.chaturbate,
|
||||||
|
twitter: vt.twitter,
|
||||||
|
patreon: vt.patreon,
|
||||||
|
twitch: vt.twitch,
|
||||||
|
tiktok: vt.tiktok,
|
||||||
|
onlyfans: vt.onlyfans,
|
||||||
|
youtube: vt.youtube,
|
||||||
|
linktree: vt.linktree,
|
||||||
|
carrd: vt.carrd,
|
||||||
|
fansly: vt.fansly,
|
||||||
|
pornhub: vt.pornhub,
|
||||||
|
discord: vt.discord,
|
||||||
|
reddit: vt.reddit,
|
||||||
|
throne: vt.throne,
|
||||||
|
instagram: vt.instagram,
|
||||||
|
facebook: vt.facebook,
|
||||||
|
merch: vt.merch,
|
||||||
|
description: `${vt.description_1 ?? ''}\n${vt.description_2 ?? ''}`.trim() || null,
|
||||||
|
themeColor: vt.theme_color,
|
||||||
|
}));
|
||||||
|
|
||||||
|
console.log('vtubers sample', vtubers.at(0))
|
||||||
|
|
||||||
|
const outPath = path.join(dataDir, 'vtubers.json');
|
||||||
|
fs.writeFileSync(outPath, JSON.stringify(vtubers, null, 2), 'utf-8');
|
||||||
|
console.log(`Migrated ${res.rows.length} vtubers → ${outPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function migrateVods() {
|
||||||
|
console.log('Migrating vods...');
|
||||||
|
const res = await v1.query(`SELECT * FROM vods`);
|
||||||
|
const vods = [];
|
||||||
|
|
||||||
|
for (const vod of res.rows) {
|
||||||
|
// Get linked vtubers
|
||||||
|
const vtuberLinks = await v1.query(
|
||||||
|
`SELECT vtuber_id FROM vods_vtuber_links WHERE vod_id = $1`,
|
||||||
|
[vod.id]
|
||||||
|
);
|
||||||
|
|
||||||
|
let vtuberIds = [];
|
||||||
|
if (vtuberLinks.rows.length > 0) {
|
||||||
|
const vtuberRes = await v1.query(
|
||||||
|
`SELECT id FROM vtubers WHERE id = ANY($1)`,
|
||||||
|
[vtuberLinks.rows.map(r => r.vtuber_id)]
|
||||||
|
);
|
||||||
|
vtuberIds = vtuberRes.rows.map(v => v.id).filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get thumbnail
|
||||||
|
const thumbLink = await v1.query(
|
||||||
|
`SELECT b2.cdn_url, b2.url FROM vods_thumbnail_links vtl
|
||||||
|
JOIN b2_files b2 ON vtl.b_2_file_id = b2.id
|
||||||
|
WHERE vtl.vod_id = $1 LIMIT 1`,
|
||||||
|
[vod.id]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get source video
|
||||||
|
const videoSrcLink = await v1.query(
|
||||||
|
`SELECT b2.cdn_url, b2.url FROM vods_video_src_b_2_links vsl
|
||||||
|
JOIN b2_files b2 ON vsl.b_2_file_id = b2.id
|
||||||
|
WHERE vsl.vod_id = $1 LIMIT 1`,
|
||||||
|
[vod.id]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get Mux asset and playback ID
|
||||||
|
const muxLink = await v1.query(
|
||||||
|
`SELECT m.asset_id, m.playback_id
|
||||||
|
FROM vods_mux_asset_links vmal
|
||||||
|
JOIN mux_assets m ON vmal.mux_asset_id = m.id
|
||||||
|
WHERE vmal.vod_id = $1 LIMIT 1`,
|
||||||
|
[vod.id]
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const muxAssetId = muxLink.rows[0]?.asset_id || null;
|
||||||
|
const muxPlaybackId = muxLink.rows[0]?.playback_id || null;
|
||||||
|
|
||||||
|
|
||||||
|
// @chatgpt we need one more
|
||||||
|
// we need to populate videoSrcB2
|
||||||
|
// we want the cdn_url from b2_files
|
||||||
|
// we have to get the link from the vods_video_src_b_2_links table
|
||||||
|
|
||||||
|
|
||||||
|
vods.push({
|
||||||
|
streamDate: vod.date ?? new Date(),
|
||||||
|
notes: vod.note,
|
||||||
|
sourceVideo: (() => {
|
||||||
|
const u = videoSrcLink.rows[0]?.cdn_url || videoSrcLink.rows[0]?.url || null;
|
||||||
|
return u ? 'content' + new URL(u).pathname : null;
|
||||||
|
})(),
|
||||||
|
thumbnail: (() => {
|
||||||
|
const u = thumbLink.rows[0]?.cdn_url || thumbLink.rows[0]?.url || null;
|
||||||
|
return u ? 'content' + new URL(u).pathname : null;
|
||||||
|
})(),
|
||||||
|
ipfsCid: vod.video_src_hash,
|
||||||
|
videoSrcB2: videoSrcLink.rows[0]?.cdn_url || null,
|
||||||
|
muxAssetId,
|
||||||
|
muxPlaybackId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(vods.at(0))
|
||||||
|
const outPath = path.join(dataDir, 'vods.json');
|
||||||
|
fs.writeFileSync(outPath, JSON.stringify(vods, null, 2), 'utf-8');
|
||||||
|
console.log(`Migrated ${res.rows.length} vods → ${outPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
await migrateVtubers();
|
||||||
|
await migrateVods();
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
} finally {
|
||||||
|
await v1.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
|
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* Migration Script: Assign missing VOD vtubers to "projektmelody"
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
* This script connects to a PocketBase instance and updates all VOD records
|
||||||
|
* that do not have a `vtuber` set, assigning them to the vtuber with slug "projektmelody".
|
||||||
|
*
|
||||||
|
* Environment variables:
|
||||||
|
* PB_URL Base URL of your PocketBase instance (e.g. "http://127.0.0.1:8090")
|
||||||
|
* PB_ADMIN_EMAIL PocketBase admin email
|
||||||
|
* PB_ADMIN_PASS PocketBase admin password
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* $ npx @dotenvx/dotenvx run -f .env.local -- node ./2025-11-05-fix-vod-vtuber.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
import PocketBase from 'pocketbase';
|
||||||
|
|
||||||
|
const pb = new PocketBase(process.env.PB_URL || 'http://127.0.0.1:8090');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log('Authenticating with PocketBase...');
|
||||||
|
await pb
|
||||||
|
.collection("_superusers")
|
||||||
|
.authWithPassword(process.env.PB_USERNAME, process.env.PB_PASSWORD);
|
||||||
|
|
||||||
|
|
||||||
|
console.log('Fetching vtuber "projektmelody"...');
|
||||||
|
const projekt = await pb.collection('vtubers').getFirstListItem('slug="projektmelody"');
|
||||||
|
if (!projekt) {
|
||||||
|
throw new Error('Could not find vtuber with slug "projektelody"');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Fetching VODs...');
|
||||||
|
let page = 1;
|
||||||
|
const perPage = 50;
|
||||||
|
let updatedCount = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const vods = await pb.collection('vods').getList(page, perPage);
|
||||||
|
if (vods.items.length === 0) break;
|
||||||
|
|
||||||
|
for (const vod of vods.items) {
|
||||||
|
// Only update if vtuber is missing or empty
|
||||||
|
if (!vod.vtubers || vod.vtubers.length === 0) {
|
||||||
|
await pb.collection('vods').update(vod.id, {
|
||||||
|
vtubers: [projekt.id],
|
||||||
|
});
|
||||||
|
console.log(`✅ Updated VOD ${vod.id} → projektmelody`);
|
||||||
|
updatedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vods.items.length < perPage) break;
|
||||||
|
page++;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Done! Updated ${updatedCount} VODs that had no vtuber set.`);
|
||||||
|
pb.authStore.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((err) => {
|
||||||
|
console.error('Migration failed:', err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
import PocketBase from 'pocketbase';
|
||||||
|
|
||||||
|
const pb = new PocketBase(process.env.PB_URL || 'http://127.0.0.1:8090');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log('Authenticating with PocketBase...');
|
||||||
|
await pb
|
||||||
|
.collection("_superusers")
|
||||||
|
.authWithPassword(process.env.PB_USERNAME, process.env.PB_PASSWORD);
|
||||||
|
|
||||||
|
const name = 'mirakink'
|
||||||
|
const vt = await pb.collection('vtubers').getFirstListItem(`slug="${name}"`);
|
||||||
|
if (!vt) {
|
||||||
|
throw new Error(`Could not find vtuber with slug "${name}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`image internals as follows`)
|
||||||
|
console.log(vt)
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
@ -0,0 +1,118 @@
|
|||||||
|
import PocketBase from 'pocketbase';
|
||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
import { basename, join } from 'node:path';
|
||||||
|
import spawn from 'nano-spawn';
|
||||||
|
import { tmpdir } from 'node:os';
|
||||||
|
import mime from 'mime';
|
||||||
|
|
||||||
|
const pb = new PocketBase(process.env.PB_URL || 'http://127.0.0.1:8090');
|
||||||
|
if (!process.env.PB_USERNAME) throw new Error('PB_USERNAME missing');
|
||||||
|
if (!process.env.PB_PASSWORD) throw new Error('PB_PASSWORD missing');
|
||||||
|
if (!process.env.B2_BUCKET_FROM) throw new Error('B2_BUCKET_FROM missing');
|
||||||
|
if (!process.env.V1_DATA_FILE) throw new Error('V1_DATA_FILE missing');
|
||||||
|
|
||||||
|
const vodsCollectionName = 'pbc_144770472';
|
||||||
|
// pbc_144770472 is the vods collection
|
||||||
|
// cv6m31vj98gmtsx is a sample vod id
|
||||||
|
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log('Authenticating with PocketBase...');
|
||||||
|
await pb
|
||||||
|
.collection("_superusers")
|
||||||
|
.authWithPassword(process.env.PB_USERNAME, process.env.PB_PASSWORD);
|
||||||
|
|
||||||
|
|
||||||
|
// Use vod.streamDate to find the correct entry
|
||||||
|
// load v1 datafile
|
||||||
|
const v1VodDataRaw = readFileSync(process.env.V1_DATA_FILE, { encoding: 'utf-8' });
|
||||||
|
const v1VodData = JSON.parse(v1VodDataRaw);
|
||||||
|
// console.log('v1VodData sample', v1VodData[0])
|
||||||
|
|
||||||
|
|
||||||
|
// # BUILD A MANIFEST
|
||||||
|
// For each vod
|
||||||
|
// Get the expected pocketbase s3 key format, ex: `pbc_144770472/cv6m31vj98gmtsx/projektmelody-fansly-2025-09-17-thumb.png`
|
||||||
|
// Get the v1 thumbnail s3 key from the v1 export json
|
||||||
|
let manifest = [];
|
||||||
|
const vods = await pb.collection('vods').getFullList();
|
||||||
|
for (const vod of vods) {
|
||||||
|
// console.log('v2VodData sample', vod)
|
||||||
|
// console.log(`for this vod`, vod)
|
||||||
|
const v1Vod = v1VodData.find((vod1) => new Date(vod1.streamDate).getTime() === new Date(vod.streamDate).getTime());
|
||||||
|
console.log(`v1vod`, v1Vod)
|
||||||
|
if (!v1Vod) throw new Error(`failed to find matching v1 data vod for vod`);
|
||||||
|
|
||||||
|
// skip if there is no thumbnail in the v1 vod
|
||||||
|
if (!v1Vod.thumbnail) continue;
|
||||||
|
const inputS3Key = v1Vod.thumbnail.replace('content/', '');
|
||||||
|
const outputFile = join(tmpdir(), basename(inputS3Key));
|
||||||
|
const entry = {
|
||||||
|
vodId: vod.id,
|
||||||
|
inputS3Key,
|
||||||
|
outputFile,
|
||||||
|
};
|
||||||
|
manifest.push(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
const invalidVods = manifest.filter((m) => m.inputS3Key.includes('mp4'))
|
||||||
|
if (invalidVods.length > 0) {
|
||||||
|
console.error('invalid thumbnails found', invalidVods)
|
||||||
|
throw new Error('invalid. mp4s found in thumbnails');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
console.log('manifest', manifest);
|
||||||
|
|
||||||
|
// # ACTUAL WORK
|
||||||
|
// Copy the file from B2_BUCKET_FROM to tmp file
|
||||||
|
// Update the pocketbase vod.thumbnail entry
|
||||||
|
|
||||||
|
function sleep(ms) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function retry(fn, retries = 6, delayMs = 500) {
|
||||||
|
let lastError;
|
||||||
|
for (let attempt = 1; attempt <= retries; attempt++) {
|
||||||
|
try {
|
||||||
|
return await fn();
|
||||||
|
} catch (err) {
|
||||||
|
lastError = err;
|
||||||
|
console.warn(`Attempt ${attempt} failed: ${err}. Retrying in ${delayMs}ms...`);
|
||||||
|
if (attempt < retries) await sleep(delayMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw lastError;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [i, m] of manifest.entries()) {
|
||||||
|
var from = "b2://" + process.env.B2_BUCKET_FROM + "/" + m.inputS3Key;
|
||||||
|
var to = m.outputFile;
|
||||||
|
var vodId = m.vodId;
|
||||||
|
|
||||||
|
console.log("processing thumbnail " + i + ". " + from + " -> " + to + " (vodId=" + vodId + ")");
|
||||||
|
|
||||||
|
// Retry the download
|
||||||
|
await retry(function () {
|
||||||
|
return spawn('b2', ['file', 'download', from, to]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Retry the PocketBase upload
|
||||||
|
await retry(async function () {
|
||||||
|
var fileData = readFileSync(m.outputFile);
|
||||||
|
var mimetype = mime.getType(m.outputFile) || undefined;
|
||||||
|
var file = new File([fileData], basename(m.outputFile), { type: mimetype });
|
||||||
|
var form = new FormData();
|
||||||
|
form.set("thumbnail", file);
|
||||||
|
return pb.collection('vods').update(vodId, form);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("All done.");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
4172
services/pocketbase/utils/data_migrations/vods.json
Normal file
4172
services/pocketbase/utils/data_migrations/vods.json
Normal file
File diff suppressed because it is too large
Load Diff
94
services/pocketbase/utils/data_migrations/vtubers.json
Normal file
94
services/pocketbase/utils/data_migrations/vtubers.json
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"slug": "projektmelody",
|
||||||
|
"displayName": "ProjektMelody",
|
||||||
|
"chaturbate": "https://chaturbate.com/projektmelody",
|
||||||
|
"twitter": "https://twitter.com/projektmelody",
|
||||||
|
"patreon": "https://www.patreon.com/projektmelody",
|
||||||
|
"twitch": "https://twitch.tv/projektmelody",
|
||||||
|
"tiktok": "https://www.tiktok.com/@realprojektmelody",
|
||||||
|
"onlyfans": "https://onlyfans.com/projektbutt",
|
||||||
|
"youtube": "https://www.youtube.com/projektmelodyofficial",
|
||||||
|
"linktree": "https://linktr.ee/projektmelody",
|
||||||
|
"carrd": null,
|
||||||
|
"fansly": "https://fansly.com/r/scienceteam",
|
||||||
|
"pornhub": "https://www.pornhub.com/model/projekt-melody",
|
||||||
|
"discord": null,
|
||||||
|
"reddit": "https://www.reddit.com/r/projektmelody/",
|
||||||
|
"throne": "https://throne.com/realprojektmelody",
|
||||||
|
"instagram": null,
|
||||||
|
"facebook": null,
|
||||||
|
"merch": "https://melody.vshojo.com/",
|
||||||
|
"description": "Also known as, \"Daddy's little grandpa,\" ProjektMelody is the pioneering hentai cam model who embodies a unique blend of sweetness and perversion. She engages with her audience with remarkable patience and politeness, demonstrating her commitment to fostering an inclusive environment.\nOn her livestreams, ProjektMelody's demeanor can range from overtly sexual to adorably reserved and self-censored. Alongside her captivating presence, she actively promotes pro-social behavior, advocates for sexual education, and emphasizes the importance of kindness.",
|
||||||
|
"themeColor": "#5C23C0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "el_xox",
|
||||||
|
"displayName": "el_XoX",
|
||||||
|
"chaturbate": "https://chaturbate.com/el_xox/",
|
||||||
|
"twitter": "https://twitter.com/el_XoX34",
|
||||||
|
"patreon": null,
|
||||||
|
"twitch": "https://www.twitch.tv/el_xox",
|
||||||
|
"tiktok": null,
|
||||||
|
"onlyfans": null,
|
||||||
|
"youtube": null,
|
||||||
|
"linktree": null,
|
||||||
|
"carrd": "https://elxox.carrd.co/",
|
||||||
|
"fansly": null,
|
||||||
|
"pornhub": null,
|
||||||
|
"discord": null,
|
||||||
|
"reddit": null,
|
||||||
|
"throne": null,
|
||||||
|
"instagram": null,
|
||||||
|
"facebook": null,
|
||||||
|
"merch": "https://elxox34.com/pages/limited-merch",
|
||||||
|
"description": null,
|
||||||
|
"themeColor": "#353FFF"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "vexruby",
|
||||||
|
"displayName": "Vexruby",
|
||||||
|
"chaturbate": "https://chaturbate.com/vexruby",
|
||||||
|
"twitter": "https://x.com/vexxxruby",
|
||||||
|
"patreon": "https://www.patreon.com/ViRoClub",
|
||||||
|
"twitch": null,
|
||||||
|
"tiktok": null,
|
||||||
|
"onlyfans": null,
|
||||||
|
"youtube": null,
|
||||||
|
"linktree": null,
|
||||||
|
"carrd": null,
|
||||||
|
"fansly": null,
|
||||||
|
"pornhub": null,
|
||||||
|
"discord": null,
|
||||||
|
"reddit": null,
|
||||||
|
"throne": null,
|
||||||
|
"instagram": null,
|
||||||
|
"facebook": null,
|
||||||
|
"merch": null,
|
||||||
|
"description": null,
|
||||||
|
"themeColor": "#f882f5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "athena_airis",
|
||||||
|
"displayName": "AIRIS ATHENA",
|
||||||
|
"chaturbate": "https://chaturbate.com/athena_airis/",
|
||||||
|
"twitter": "https://twitter.com/Athena_Airis",
|
||||||
|
"patreon": null,
|
||||||
|
"twitch": "https://www.twitch.tv/athena_airis",
|
||||||
|
"tiktok": "https://tiktok.com/@athena_airis",
|
||||||
|
"onlyfans": null,
|
||||||
|
"youtube": "https://www.youtube.com/channel/UClizqUqSuy26sO01qsPh5bw",
|
||||||
|
"linktree": "https://linktr.ee/athenaairis",
|
||||||
|
"carrd": null,
|
||||||
|
"fansly": "https://fansly.com/Athena-Airis",
|
||||||
|
"pornhub": null,
|
||||||
|
"discord": "https://discord.gg/eXPTgua",
|
||||||
|
"reddit": null,
|
||||||
|
"throne": null,
|
||||||
|
"instagram": "https://instagram.com/theathenaairis",
|
||||||
|
"facebook": null,
|
||||||
|
"merch": null,
|
||||||
|
"description": null,
|
||||||
|
"themeColor": "#dde1ec"
|
||||||
|
}
|
||||||
|
]
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user