mirror of
https://github.com/pockethost/pockethost.git
synced 2025-03-30 15:08:30 +00:00
Merge branch 'main' of github.com:pockethost/pockethost
This commit is contained in:
commit
e87154dc68
@ -2,10 +2,8 @@
|
||||
"$schema": "https://unpkg.com/@changesets/config@3.0.1/schema.json",
|
||||
"changelog": "@changesets/cli/changelog",
|
||||
"commit": false,
|
||||
"fixed": [],
|
||||
"linked": [],
|
||||
"access": "restricted",
|
||||
"baseBranch": "master",
|
||||
"access": "public",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": []
|
||||
}
|
||||
|
14
.changeset/pre.json
Normal file
14
.changeset/pre.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"mode": "pre",
|
||||
"tag": "rc",
|
||||
"initialVersions": {
|
||||
"@pockethost/dashboard": "0.4.1",
|
||||
"pockethost": "1.7.0",
|
||||
"pockethost-instance": "0.0.1"
|
||||
},
|
||||
"changesets": [
|
||||
"funny-radios-behave",
|
||||
"rare-beds-pull",
|
||||
"tame-needles-play"
|
||||
]
|
||||
}
|
5
.changeset/rare-beds-pull.md
Normal file
5
.changeset/rare-beds-pull.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'pockethost': major
|
||||
---
|
||||
|
||||
This release is a checkpoint release to jump over v1, which has been abandoned. v0 ended up becoming v2.
|
15
.cursorrules
Normal file
15
.cursorrules
Normal file
@ -0,0 +1,15 @@
|
||||
# Project Context
|
||||
|
||||
- This project is about PocketBase, the PocketBase JS client, and the PocketBase JSVM.
|
||||
|
||||
# Programming Style
|
||||
|
||||
- Prefer factory functions that return an API object instead of classes
|
||||
- Prefer early returns
|
||||
|
||||
# Generating Markdown
|
||||
|
||||
- When proposing an edit to a markdown file, first decide if there will be code snippets in the markdown file.
|
||||
- If there are no code snippets, wrap the beginning and end of your answer in backticks and markdown as the language.
|
||||
- If there are code snippets, indent the code snippets with two spaces and the correct language for proper rendering. Indentations level 0 and 4 is not allowed.
|
||||
- If a markdown code block is indented with any value other than 2 spaces, automatically fix it
|
4
.github/workflows/publish-dashboard.yaml
vendored
4
.github/workflows/publish-dashboard.yaml
vendored
@ -10,7 +10,7 @@ on:
|
||||
|
||||
env:
|
||||
PUBLIC_APEX_DOMAIN: ${{ vars.PUBLIC_APEX_DOMAIN }}
|
||||
MAIN_BRANCH: v0/main
|
||||
MAIN_BRANCH: main
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
@ -50,7 +50,7 @@ jobs:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
projectName: pockethost-lander
|
||||
projectName: pockethost
|
||||
directory: ./packages/dashboard/build
|
||||
branch: ${{ github.head_ref || github.ref_name }}
|
||||
wranglerVersion: '3'
|
||||
|
@ -1,5 +0,0 @@
|
||||
branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||
if [ "$branch" = "master" ]; then
|
||||
npx --no -- commitlint --edit $1
|
||||
fi
|
||||
|
@ -1,6 +0,0 @@
|
||||
# branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||
# if [ "$branch" = "v0/main" ]; then
|
||||
# pnpm -r check:types
|
||||
# pnpm -r build
|
||||
# fi
|
||||
|
@ -16,6 +16,7 @@
|
||||
"esbuild",
|
||||
"eventsource",
|
||||
"Flouder's",
|
||||
"Frontends",
|
||||
"fullchain",
|
||||
"geoip",
|
||||
"getenv",
|
||||
@ -35,6 +36,7 @@
|
||||
"mothership",
|
||||
"multitenancy",
|
||||
"multitenant",
|
||||
"neverending",
|
||||
"noaxis",
|
||||
"nofile",
|
||||
"nohup",
|
||||
|
@ -16,6 +16,7 @@
|
||||
},
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@changesets/cli": "^2.27.9",
|
||||
"prettier": "^3.0.3",
|
||||
"prettier-plugin-jsdoc": "^1.3.0",
|
||||
"prettier-plugin-organize-imports": "^4.1.0",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@pockethost/dashboard",
|
||||
"version": "1.0.0-rc.1",
|
||||
"version": "0.4.1",
|
||||
"private": true,
|
||||
"main": "./src/app.html",
|
||||
"scripts": {
|
||||
@ -8,6 +8,7 @@
|
||||
"preview": "npx http-server@latest ./build -P \"http://localhost:8080?\"",
|
||||
"dev": "vite dev",
|
||||
"build": "NODE_ENV=production vite build",
|
||||
"deploy": "wrangler pages deploy ./build",
|
||||
"lint": "prettier --check .",
|
||||
"format": "prettier --write .",
|
||||
"postbuild": "svelte-sitemap --domain https://pockethost.io"
|
||||
@ -47,6 +48,7 @@
|
||||
"svelte-sitemap": "^2.6.0",
|
||||
"tailwindcss": "^3.4.13",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vite": "^5.4.8"
|
||||
"vite": "^5.4.8",
|
||||
"wrangler": "^3.87.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { browser } from '$app/environment'
|
||||
import { INSTANCE_URL } from '$src/env'
|
||||
import { createGenericSyncEvent } from '$util/events'
|
||||
import { fetchEventSource } from '@microsoft/fetch-event-source'
|
||||
@ -210,13 +211,15 @@ export const createPocketbaseClient = (config: PocketbaseClientConfig) => {
|
||||
* token may be out of date, or fields in the user record may have changed
|
||||
* in the backend.
|
||||
*/
|
||||
refreshAuthToken()
|
||||
.catch((error) => {
|
||||
client.authStore.clear()
|
||||
})
|
||||
.finally(() => {
|
||||
fireAuthChange(client.authStore)
|
||||
})
|
||||
if (browser) {
|
||||
refreshAuthToken()
|
||||
.catch((error) => {
|
||||
client.authStore.clear()
|
||||
})
|
||||
.finally(() => {
|
||||
fireAuthChange(client.authStore)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for auth state changes and subscribe to realtime _user events.
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { userSubscriptionType } from '$util/stores'
|
||||
import { PLAN_NAMES, SubscriptionType } from 'pockethost'
|
||||
import { PLAN_NAMES, SubscriptionType } from 'pockethost/common'
|
||||
import { userStore } from '$util/stores'
|
||||
import { onMount } from 'svelte'
|
||||
import FlounderCard from './FlounderCard.svelte'
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { PLAN_NAMES, SubscriptionType } from 'pockethost'
|
||||
import { PLAN_NAMES, SubscriptionType } from 'pockethost/common'
|
||||
import { stats, userStore } from '$util/stores'
|
||||
import PricingCard from '$src/routes/(static)/pricing/PricingCard.svelte'
|
||||
|
||||
@ -15,6 +15,11 @@
|
||||
]
|
||||
export let startDate: Date | null = null
|
||||
export let endDate: Date | null = null
|
||||
|
||||
const logInFirst = () => {
|
||||
alert('Please log in and verify your account first.')
|
||||
window.location.href = '/login'
|
||||
}
|
||||
</script>
|
||||
|
||||
<PricingCard
|
||||
@ -27,6 +32,7 @@
|
||||
description="Epic elite! The Flounder's Edition is almost as good as the Founder's edition, and you'll help PocketHost go global."
|
||||
{priceMonthly}
|
||||
{priceAnnually}
|
||||
requireAuthenticatedUser
|
||||
checkoutMonthURL="https://store.pockethost.io/buy/9ff8775b-6b9e-4aa8-a0ab-dc5e58e25408?checkout[custom][user_id]={$userStore?.id}&checkout[email]={$userStore?.email}&checkout[discount_code]=G0MTI0OQ"
|
||||
checkoutYearURL="https://store.pockethost.io/buy/82d79f7c-64f6-4c2b-9f58-dcc8951f1cdd?checkout[custom][user_id]={$userStore?.id}&checkout[email]={$userStore?.email}&checkout[discount_code]=G0MTI0OQ"
|
||||
features={[
|
||||
|
@ -3,7 +3,7 @@
|
||||
import { globalInstancesStore, userSubscriptionType } from '$util/stores'
|
||||
import { values } from '@s-libs/micro-dash'
|
||||
import InstanceList from './InstanceList.svelte'
|
||||
import { SubscriptionType } from 'pockethost'
|
||||
import { SubscriptionType } from 'pockethost/common'
|
||||
import { faPlus } from '@fortawesome/free-solid-svg-icons'
|
||||
import Fa from 'svelte-fa'
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
import { globalInstancesStore } from '$util/stores'
|
||||
import { faCircleInfo } from '@fortawesome/free-solid-svg-icons'
|
||||
import { values } from '@s-libs/micro-dash'
|
||||
import { type InstanceId } from 'pockethost'
|
||||
import { type InstanceId } from 'pockethost/common'
|
||||
import Fa from 'svelte-fa'
|
||||
|
||||
const { updateInstance } = client()
|
||||
@ -26,7 +26,7 @@
|
||||
{#each values($globalInstancesStore).sort( (a, b) => a.subdomain.localeCompare(b.subdomain), ) as instance, index}
|
||||
<button
|
||||
class={`card min-w-80 lg:max-w-80 flex-1 m-4 transition hover:bg-base-300 ${instance.maintenance ? 'bg-base-200' : 'bg-neutral'}`}
|
||||
on:click={_=>goto(`/instances/${instance.id}`)}
|
||||
on:click={(_) => goto(`/instances/${instance.id}`)}
|
||||
>
|
||||
<div class="card-body w-full">
|
||||
<div class="card-title">
|
||||
@ -38,7 +38,7 @@
|
||||
? 'bg-red-500 hover:bg-red-500'
|
||||
: 'toggle-success'}"
|
||||
checked={!instance.maintenance}
|
||||
on:click={e=>e.stopPropagation()}
|
||||
on:click={(e) => e.stopPropagation()}
|
||||
on:change={handleMaintenanceChange(instance.id)}
|
||||
/>
|
||||
</div>
|
||||
|
@ -4,7 +4,7 @@
|
||||
import { client } from '$src/pocketbase-client'
|
||||
import { instance } from '../store'
|
||||
import { reduce } from '@s-libs/micro-dash'
|
||||
import { logger, type UpdateInstancePayload } from 'pockethost'
|
||||
import { logger, type UpdateInstancePayload } from 'pockethost/common'
|
||||
import Fa from 'svelte-fa'
|
||||
import { faTrash } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
|
@ -1,52 +1,57 @@
|
||||
<script>
|
||||
// Define the width of the sidebar
|
||||
const sidebarWidth = '300px'
|
||||
import { page } from '$app/stores';
|
||||
import { page } from '$app/stores'
|
||||
import DocLink from './DocLink.svelte'
|
||||
</script>
|
||||
|
||||
<div class="container-fluid mt-4 flex gap-6 flex-col-reverse md:flex-row">
|
||||
<div class="w-full md:w-fit bg-base-200 rounded-r-lg py-4">
|
||||
<ul class="menu text-base">
|
||||
<li class="menu-title">Overview</li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs")} href="/docs">Introduction</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/getting-started")} href="/docs/getting-started">Getting Started</a></li>
|
||||
<DocLink path="introduction" title="Introduction" />
|
||||
<DocLink path="getting-started" title="Getting Started" />
|
||||
</ul>
|
||||
<ul class="menu">
|
||||
<li class="menu-title">Instance Management</li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/create")} href="/docs/create">Create</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/accessing")} href="/docs/accessing">Connect</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/power")} href="/docs/power">Power</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/rename-instance")} href="/docs/rename-instance">Rename</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/delete")} href="/docs/delete">Delete</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/limits")} href="/docs/limits">Limits</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/smtp")} href="/docs/smtp">Outgoing Email</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/versions")} href="/docs/versions">Changing Versions</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/s3")} href="/docs/s3">Using S3 Storage</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/custiom-binaries")} href="/docs/custom-binaries">Custom Binaries</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/custom-domains")} href="/docs/custom-domains">Custom Domains</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/backup-restore")} href="/docs/backup-restore">Backup/Restore</a></li>
|
||||
<DocLink path="create" title="Create" />
|
||||
<DocLink path="accessing" title="Connect" />
|
||||
<DocLink path="power" title="Power" />
|
||||
<DocLink path="rename-instance" title="Rename" />
|
||||
<DocLink path="delete" title="Delete" />
|
||||
<DocLink path="limits" title="Limits" />
|
||||
<DocLink path="smtp" title="Outgoing Email" />
|
||||
<DocLink path="versions" title="Changing Versions" />
|
||||
<DocLink path="s3" title="Using S3 Storage" />
|
||||
<DocLink path="custom-binaries" title="Custom Binaries" />
|
||||
<DocLink path="custom-domains" title="Custom Domains" />
|
||||
<DocLink path="backup-restore" title="Backup/Restore" />
|
||||
</ul>
|
||||
<ul class="menu">
|
||||
<li class="menu-title">Daily Use Guide</li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/logs")} href="/docs/logs">Logging</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/dev-mode")} href="/docs/dev-mode">Dev Mode</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/secrets")} href="/docs/secrets">Secrets</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/ftp")} href="/docs/ftp">FTP Access</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/admin-sync")} href="/docs/admin-sync">Admin Sync</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/js")} href="/docs/js">Extending via JS</a></li>
|
||||
<DocLink path="logs" title="Logging" />
|
||||
<DocLink path="dev-mode" title="Dev Mode" />
|
||||
<DocLink path="secrets" title="Secrets" />
|
||||
<DocLink path="ftp" title="FTP Access" />
|
||||
<DocLink path="admin-sync" title="Admin Sync" />
|
||||
<DocLink path="js" title="Extending via JS" />
|
||||
</ul>
|
||||
<ul class="menu">
|
||||
<li class="menu-title">Programming Guide</li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/programming")} href="/docs/programming">Frontends and JS Hooks</a></li>
|
||||
<DocLink path="programming" title="Frontends and JS Hooks" />
|
||||
<DocLink
|
||||
path="server-side-pocketbase-antipattern"
|
||||
title="Server-Side PocketBase is an Anti-Pattern"
|
||||
/>
|
||||
</ul>
|
||||
<ul class="menu">
|
||||
<li class="menu-title">Appendix</li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/account-creation")} href="/docs/account-creation">Account Creation</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/pricing-ethos")} href="/docs/pricing-ethos">Pricing Ethos</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/faq")} href="/docs/faq">FAQ</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/gs-gmail")} href="/docs/gs-gmail">Google Suite Gmail Setup</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/ses")} href="/docs/ses">Amazon SES Setup</a></li>
|
||||
<li><a class:active={$page.url.pathname.endsWith("/docs/self-hosting")} href="/docs/self-hosting">Self-Hosting</a></li>
|
||||
<DocLink path="account-creation" title="Account Creation" />
|
||||
<DocLink path="pricing-ethos" title="Pricing Ethos" />
|
||||
<DocLink path="faq" title="FAQ" />
|
||||
<DocLink path="gs-gmail" title="Google Suite Gmail Setup" />
|
||||
<DocLink path="ses" title="Amazon SES Setup" />
|
||||
<DocLink path="self-hosting" title="Self-Hosting" />
|
||||
</ul>
|
||||
</div>
|
||||
<div class="docs-content prose p-5 w-full md:w-fit md:max-w-[50%]">
|
||||
@ -55,7 +60,8 @@
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.menu, .menu-title {
|
||||
@apply text-base
|
||||
.menu,
|
||||
.menu-title {
|
||||
@apply text-base;
|
||||
}
|
||||
</style>
|
||||
|
18
packages/dashboard/src/routes/(static)/docs/DocLink.svelte
Normal file
18
packages/dashboard/src/routes/(static)/docs/DocLink.svelte
Normal file
@ -0,0 +1,18 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores'
|
||||
|
||||
export let path: string
|
||||
export let title: string
|
||||
|
||||
$: isActive = $page.url.pathname.endsWith(`/docs/${path}`)
|
||||
</script>
|
||||
|
||||
<li class:active={isActive} class="w-48">
|
||||
<a href={`/docs/${path}`}>{title}</a>
|
||||
</li>
|
||||
|
||||
<style lang="scss">
|
||||
li.active {
|
||||
@apply bg-neutral rounded-md;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,74 @@
|
||||
---
|
||||
title: Server-Side PocketBase is an Anti-Pattern
|
||||
category: programming
|
||||
description: Using PocketBase from SvelteKit or Next.js server files is generally an antipattern.
|
||||
---
|
||||
|
||||
# Server-Side PocketBase is an Anti-Pattern
|
||||
|
||||
When building applications with PocketBase, it's tempting to access it from server-side code in frameworks like SvelteKit or Next.js. However, this approach often indicates architectural issues and should generally be avoided. Here's why:
|
||||
|
||||
## The Problem with Server-Side Access
|
||||
|
||||
### Double Network Hops
|
||||
|
||||
When you access PocketBase from your server-side code, requests make two network hops:
|
||||
|
||||
1. Client -> Your Server
|
||||
2. Your Server -> PocketBase
|
||||
|
||||
This adds unnecessary latency compared to direct client-to-PocketBase communication.
|
||||
|
||||
### JWT State Management Complexity
|
||||
|
||||
Managing authentication state becomes more complex when you need to transfer JWT tokens between client and server. This often leads to security vulnerabilities when not handled properly.
|
||||
|
||||
### Rate Limiting Issues
|
||||
|
||||
Accessing PocketBase from a single backend IP address can trigger rate limits more easily than distributed client access. This is intentionally designed to encourage direct client communication.
|
||||
|
||||
## Better Approaches
|
||||
|
||||
### Use Direct Client Access
|
||||
|
||||
PocketBase is designed to be accessed directly from client applications. Its built-in security rules provide fine-grained access control without needing a middleware server.
|
||||
|
||||
### Leverage JS Hooks for Privileged Operations
|
||||
|
||||
If you need server-side logic or privileged operations, use PocketBase's JS hooks feature instead of wrapping PocketBase calls in a separate backend:
|
||||
|
||||
- Create custom API endpoints using JS hooks
|
||||
- Handle privileged operations directly in PocketBase
|
||||
- Implement business logic where it belongs
|
||||
|
||||
### Consider Static Site Generation
|
||||
|
||||
If you're using server-side rendering primarily to protect PocketBase access, consider:
|
||||
|
||||
- Moving to static site generation (SSG)
|
||||
- Using PocketBase's security rules for protection
|
||||
- Implementing sensitive operations via JS hooks
|
||||
|
||||
## When Server-Side Access Makes Sense
|
||||
|
||||
While generally an anti-pattern, there are valid cases for server-side PocketBase access:
|
||||
|
||||
- Complex data aggregation requiring server resources
|
||||
- Integration with external services that can't be exposed to clients
|
||||
- Specific security requirements that can't be met with API rules
|
||||
|
||||
However, even in these cases, consider whether the functionality could be implemented using PocketBase's native features first.
|
||||
|
||||
## Further Reading
|
||||
|
||||
Gani also posted about this on the PocketBase site: [JS SSR - issues and recommendations when interacting with PocketBase](https://github.com/pocketbase/pocketbase/discussions/5313)
|
||||
|
||||
## Conclusion
|
||||
|
||||
PocketBase is designed to be a complete backend solution with built-in security and extensibility. Adding an additional server layer often complicates the architecture unnecessarily. Before implementing server-side PocketBase access, consider whether you can:
|
||||
|
||||
1. Use client-side access with security rules
|
||||
2. Implement the functionality via JS hooks
|
||||
3. Restructure your application to leverage PocketBase's native capabilities
|
||||
|
||||
This will lead to simpler, more maintainable, and more performant applications.
|
@ -7,6 +7,7 @@
|
||||
import NewInstanceProcessingBlock from './NewInstanceProcessingBlock.svelte'
|
||||
import Fa from 'svelte-fa'
|
||||
import { faArrowRight, faRotate } from '@fortawesome/free-solid-svg-icons'
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
export let isSignUpView: boolean = false
|
||||
let isProcessing: boolean = false
|
||||
@ -30,34 +31,38 @@
|
||||
instanceNameField.set(name)
|
||||
}
|
||||
|
||||
instanceNameField.subscribe(async (name) => {
|
||||
if (name === $instanceInfo.name) return
|
||||
try {
|
||||
instanceInfo.update((info) => ({
|
||||
...info,
|
||||
fetching: true,
|
||||
}))
|
||||
const res = await client().client.send(
|
||||
`/api/signup?name=${encodeURIComponent(name)}`,
|
||||
{},
|
||||
)
|
||||
instanceInfo.update((info) => ({
|
||||
...info,
|
||||
fetching: false,
|
||||
available: true,
|
||||
name,
|
||||
}))
|
||||
} catch (e) {
|
||||
instanceInfo.update((info) => ({
|
||||
...info,
|
||||
fetching: false,
|
||||
available: false,
|
||||
name,
|
||||
}))
|
||||
}
|
||||
onMount(() => {
|
||||
instanceNameField.subscribe(async (name) => {
|
||||
if (name === $instanceInfo.name) return
|
||||
try {
|
||||
instanceInfo.update((info) => ({
|
||||
...info,
|
||||
fetching: true,
|
||||
}))
|
||||
const res = await client().client.send(
|
||||
`/api/signup?name=${encodeURIComponent(name)}`,
|
||||
{},
|
||||
)
|
||||
instanceInfo.update((info) => ({
|
||||
...info,
|
||||
fetching: false,
|
||||
available: true,
|
||||
name,
|
||||
}))
|
||||
} catch (e) {
|
||||
instanceInfo.update((info) => ({
|
||||
...info,
|
||||
fetching: false,
|
||||
available: false,
|
||||
name,
|
||||
}))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
generateSlug()
|
||||
onMount(() => {
|
||||
generateSlug()
|
||||
})
|
||||
|
||||
// Set up the variables to hold the form information
|
||||
let email: string = ''
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import PricingCard from '$src/routes/(static)/pricing/PricingCard.svelte'
|
||||
import { PLAN_NAMES, SubscriptionType } from 'pockethost'
|
||||
import { PLAN_NAMES, SubscriptionType } from 'pockethost/common'
|
||||
import PricingTable from './PricingTable.svelte'
|
||||
import { userStore } from '$util/stores'
|
||||
import FlounderCard from '$src/routes/(app)/account/FlounderCard.svelte'
|
||||
|
@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { faClock, faCheck, faTimes } from '@fortawesome/free-solid-svg-icons'
|
||||
import { userStore } from '$util/stores'
|
||||
import Fa from 'svelte-fa'
|
||||
import { onMount, onDestroy } from 'svelte'
|
||||
import { writable } from 'svelte/store'
|
||||
@ -18,6 +19,7 @@
|
||||
export let fundingGoals: string[] = []
|
||||
export let startDate: Date | null = null
|
||||
export let endDate: Date | null = null
|
||||
export let requireAuthenticatedUser = false
|
||||
|
||||
const comingSoon = startDate && startDate > new Date()
|
||||
|
||||
@ -73,6 +75,9 @@
|
||||
} else if (qtyRemaining <= 0) {
|
||||
event.preventDefault()
|
||||
alert(soldOutText)
|
||||
} else if (requireAuthenticatedUser && !$userStore?.verified) {
|
||||
event.preventDefault()
|
||||
alert('Please create and verify your account first.')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -3,18 +3,16 @@
|
||||
</script>
|
||||
|
||||
{#if !$isMothershipReachable}
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
|
||||
<div class="alert alert-error max-w-2xl mx-4">
|
||||
<div class="text-center">
|
||||
<h2 class="text-xl font-bold mb-2">Connection Lost</h2>
|
||||
<p>
|
||||
The PocketHost mothership is temporarily unreachable. Your instances
|
||||
are safe and still reachable. Go to the <a
|
||||
href="https://status.pockethost.io"
|
||||
class="link font-semibold underline">status page</a
|
||||
> for more information.
|
||||
</p>
|
||||
</div>
|
||||
<div class="bg-error text-error-content p-4 rounded-none">
|
||||
<div class="text-center">
|
||||
<h2 class="text-xl font-bold mb-2">Connection Lost</h2>
|
||||
<p>
|
||||
The PocketHost mothership is temporarily unreachable. Your instances are
|
||||
safe and still reachable. Go to the <a
|
||||
href="https://status.pockethost.io"
|
||||
class="link font-semibold underline">status page</a
|
||||
> for more information.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -1,16 +1,20 @@
|
||||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
<div class="alert alert-info bg-yellow-300 rounded-none">
|
||||
<div class="text-info-content">
|
||||
This promo banner will go away after Dec 2. Please spread the word and help
|
||||
close the Flouder's round.
|
||||
</div>
|
||||
<div class="alert alert-info bg-yellow-300 rounded-none mb-10">
|
||||
<div>
|
||||
<a
|
||||
href="https://www.producthunt.com/posts/pockethost?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-pockethost"
|
||||
class="btn btn-sm btn-neutral">Vote on Product Hunt</a
|
||||
>
|
||||
<a href="/pricing" class="btn btn-sm btn-neutral">Black Friday ON now!</a>
|
||||
<div class="text-info-content">
|
||||
This promo banner will go away after Dec 2. Please spread the word and
|
||||
help close the Flouder's round.
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
href="https://www.producthunt.com/posts/pockethost?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-pockethost"
|
||||
class="btn btn-sm btn-neutral m-2">Vote on Product Hunt</a
|
||||
>
|
||||
<a href="/pricing" class="btn btn-sm btn-neutral m-2"
|
||||
>Black Friday ON now!</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -41,21 +41,33 @@ export const stats = writable<{
|
||||
total_flounder_subscribers: 0,
|
||||
})
|
||||
|
||||
const checkStats = () => {
|
||||
client()
|
||||
.client.send(`/api/stats`, {})
|
||||
.then((res) => {
|
||||
stats.set(res)
|
||||
isMothershipReachable.set(true)
|
||||
// setTimeout(checkStats, 1000 * 60 * 5)
|
||||
})
|
||||
.catch(() => {
|
||||
// isMothershipReachable.set(false)
|
||||
// setTimeout(checkStats, 1000)
|
||||
})
|
||||
}
|
||||
|
||||
const continuouslyCheckMothershipReachability = () => {
|
||||
setInterval(() => {
|
||||
client()
|
||||
.client.send(`/api/health`, {})
|
||||
.then((res) => {
|
||||
isMothershipReachable.set(true)
|
||||
})
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
export const init = () => {
|
||||
const { onAuthChange } = client()
|
||||
|
||||
const checkStats = () => {
|
||||
client()
|
||||
.client.send(`/api/stats`, {})
|
||||
.then((res) => {
|
||||
stats.set(res)
|
||||
setTimeout(checkStats, 1000 * 60 * 5)
|
||||
})
|
||||
.catch(() => {
|
||||
isMothershipReachable.set(false)
|
||||
setTimeout(checkStats, 1000)
|
||||
})
|
||||
}
|
||||
checkStats()
|
||||
|
||||
onAuthChange((authStoreProps) => {
|
||||
@ -121,15 +133,4 @@ export const init = () => {
|
||||
}
|
||||
tryInstanceSubscribe()
|
||||
})
|
||||
|
||||
setInterval(() => {
|
||||
client()
|
||||
.client.send(`/api/health`, {})
|
||||
.then((res) => {
|
||||
isMothershipReachable.set(true)
|
||||
})
|
||||
.catch(() => {
|
||||
isMothershipReachable.set(false)
|
||||
})
|
||||
}, 5000)
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ const config: UserConfig = {
|
||||
server: {
|
||||
port: 5174,
|
||||
strictPort: true,
|
||||
host: 'app.pockethost.lvh.me',
|
||||
host: 'pockethost.lvh.me',
|
||||
hmr: {
|
||||
clientPort: 5174,
|
||||
},
|
||||
|
@ -2,6 +2,7 @@
|
||||
"name": "pockethost-instance",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "docker build . -t benallfree/pockethost-instance:${npm_package_version} -t benallfree/pockethost-instance:latest"
|
||||
"build": "docker build . -t benallfree/pockethost-instance:${npm_package_version} -t benallfree/pockethost-instance:latest",
|
||||
"push": "docker push benallfree/pockethost-instance:${npm_package_version} && docker push benallfree/pockethost-instance:latest"
|
||||
}
|
||||
}
|
||||
|
5
packages/pockethost/CHANGELOG.md
Normal file
5
packages/pockethost/CHANGELOG.md
Normal file
@ -0,0 +1,5 @@
|
||||
# pockethost
|
||||
|
||||
## 2.0.0-rc.0
|
||||
|
||||
This release is a checkpoint release to jump over v1, which has been abandoned. v0 ended up becoming v2.
|
@ -1 +0,0 @@
|
||||
export * from './src/common'
|
@ -1,2 +0,0 @@
|
||||
export * from './src/common'
|
||||
export * from './src/core'
|
@ -1,13 +1,19 @@
|
||||
{
|
||||
"name": "pockethost",
|
||||
"version": "0.12.0",
|
||||
"version": "2.0.0-rc.0",
|
||||
"author": {
|
||||
"name": "Ben Allfree",
|
||||
"url": "https://github.com/benallfree"
|
||||
},
|
||||
"license": "MIT",
|
||||
"main": "./common.ts",
|
||||
"module": "./common.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./src/index.ts"
|
||||
},
|
||||
"./common": {
|
||||
"import": "./src/common/index.ts"
|
||||
}
|
||||
},
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"pockethost": "src/cli/index.ts"
|
||||
@ -72,5 +78,11 @@
|
||||
"@types/tail": "^2.2.3",
|
||||
"@types/unzipper": "^0.10.8",
|
||||
"@types/vhost": "^3.0.9"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"CHANGELOG.md",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
]
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
import Dockerode from 'dockerode'
|
||||
import { ErrorRequestHandler } from 'express'
|
||||
import { logger } from '../../../../../common'
|
||||
import {
|
||||
MOTHERSHIP_ADMIN_PASSWORD,
|
||||
MOTHERSHIP_ADMIN_USERNAME,
|
||||
MOTHERSHIP_URL,
|
||||
discordAlert,
|
||||
logger,
|
||||
neverendingPromise,
|
||||
tryFetch,
|
||||
} from '../../../../../core'
|
||||
} from '../../../../..'
|
||||
import {
|
||||
DOCKER_INSTANCE_IMAGE_NAME,
|
||||
MothershipAdminClientService,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Command } from 'commander'
|
||||
import { logger } from '../../../../../../common'
|
||||
import { logger } from '../../../../../common'
|
||||
import { daemon } from './daemon'
|
||||
|
||||
type Options = {
|
||||
|
@ -4,6 +4,7 @@ import { spawn } from 'child_process'
|
||||
import { Mode, constants, createReadStream, createWriteStream } from 'fs'
|
||||
import { FileStat, FileSystem, FtpConnection } from 'ftp-srv'
|
||||
import { dirname, isAbsolute, join, normalize, resolve, sep } from 'path'
|
||||
import { DATA_ROOT } from '../../../../..'
|
||||
import {
|
||||
InstanceFields,
|
||||
Logger,
|
||||
@ -11,8 +12,10 @@ import {
|
||||
assert,
|
||||
seqid,
|
||||
} from '../../../../../common'
|
||||
import { DATA_ROOT } from '../../../../../core'
|
||||
import { InstanceLogger, InstanceLoggerApi } from '../../../../../services'
|
||||
import {
|
||||
InstanceLogWriter,
|
||||
InstanceLogWriterApi,
|
||||
} from '../../../../../services'
|
||||
import * as fsAsync from './fs-async'
|
||||
import { MAINTENANCE_ONLY_INSTANCE_ROOTS } from './guards'
|
||||
|
||||
@ -48,7 +51,7 @@ const checkBun = (
|
||||
[`bun.lockb`, `package.json`].includes(maybeImportant || ''))
|
||||
|
||||
if (isImportant) {
|
||||
const logger = InstanceLogger(instance.id, `exec`, { ttl: 5000 })
|
||||
const logger = InstanceLogWriter(instance.id, `exec`)
|
||||
logger.info(`${maybeImportant} changed, running bun install`)
|
||||
launchBunInstall(instance, virtualPath, cwd).catch(console.error)
|
||||
}
|
||||
@ -56,7 +59,7 @@ const checkBun = (
|
||||
|
||||
const runBun = (() => {
|
||||
const bunLimiter = new Bottleneck({ maxConcurrent: 1 })
|
||||
return (cwd: string, logger: InstanceLoggerApi) =>
|
||||
return (cwd: string, logger: InstanceLogWriterApi) =>
|
||||
bunLimiter.schedule(
|
||||
() =>
|
||||
new Promise<number | null>((resolve) => {
|
||||
@ -100,7 +103,7 @@ const launchBunInstall = (() => {
|
||||
runCache[cwd] = { runAgain: true }
|
||||
while (runCache[cwd]!.runAgain) {
|
||||
runCache[cwd]!.runAgain = false
|
||||
const logger = InstanceLogger(instance.id, `exec`)
|
||||
const logger = InstanceLogWriter(instance.id, `exec`)
|
||||
logger.info(`Launching 'bun install' in ${virtualPath}`)
|
||||
await runBun(cwd, logger)
|
||||
}
|
||||
|
@ -1,11 +1,5 @@
|
||||
import { readFileSync } from 'fs'
|
||||
import { FtpSrv } from 'ftp-srv'
|
||||
import {
|
||||
PocketBase,
|
||||
logger,
|
||||
mergeConfig,
|
||||
mkSingleton,
|
||||
} from '../../../../../common'
|
||||
import {
|
||||
MOTHERSHIP_URL,
|
||||
PH_FTP_PASV_IP,
|
||||
@ -15,7 +9,13 @@ import {
|
||||
SSL_CERT,
|
||||
SSL_KEY,
|
||||
asyncExitHook,
|
||||
} from '../../../../../core'
|
||||
} from '../../../../..'
|
||||
import {
|
||||
PocketBase,
|
||||
logger,
|
||||
mergeConfig,
|
||||
mkSingleton,
|
||||
} from '../../../../../common'
|
||||
import { PhFs } from './PhFs'
|
||||
|
||||
export type FtpConfig = { mothershipUrl: string }
|
||||
|
@ -1,9 +1,5 @@
|
||||
import { MOTHERSHIP_URL, neverendingPromise, tryFetch } from '../../../../..'
|
||||
import { logger } from '../../../../../common'
|
||||
import {
|
||||
MOTHERSHIP_URL,
|
||||
neverendingPromise,
|
||||
tryFetch,
|
||||
} from '../../../../../core'
|
||||
import { ftpService } from '../FtpService'
|
||||
|
||||
export async function ftp() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Command } from 'commander'
|
||||
import { logger } from '../../../../../../common'
|
||||
import { logger } from '../../../../../common'
|
||||
import { ftp } from './ftp'
|
||||
|
||||
type Options = {
|
||||
|
@ -13,7 +13,7 @@ export const createIpWhitelistMiddleware = (blockedCIDRs: string[]) => {
|
||||
const ip = req.ip // or req.headers['x-forwarded-for'] || req.connection.remoteAddress;
|
||||
if (
|
||||
blockedCIDRs.length === 0 ||
|
||||
blockedCIDRObjects.some((cidr) => cidr.contains(ip))
|
||||
(ip && blockedCIDRObjects.some((cidr) => cidr.contains(ip)))
|
||||
) {
|
||||
next()
|
||||
} else {
|
||||
|
@ -7,7 +7,6 @@ import fs from 'fs'
|
||||
import http from 'http'
|
||||
import { createProxyMiddleware } from 'http-proxy-middleware'
|
||||
import https from 'https'
|
||||
import { logger } from '../../../../../common/Logger'
|
||||
import {
|
||||
APEX_DOMAIN,
|
||||
DAEMON_PORT,
|
||||
@ -18,7 +17,8 @@ import {
|
||||
neverendingPromise,
|
||||
SSL_CERT,
|
||||
SSL_KEY,
|
||||
} from '../../../../../core'
|
||||
} from '../../../../..'
|
||||
import { logger } from '../../../../../common/Logger'
|
||||
import { createIpWhitelistMiddleware } from './cidr'
|
||||
import { createVhostProxyMiddleware } from './createVhostProxyMiddleware'
|
||||
|
||||
@ -31,7 +31,7 @@ export const firewall = async () => {
|
||||
const DEV_ROUTES = {
|
||||
[`mail.${APEX_DOMAIN()}`]: `http://localhost:${1080}`,
|
||||
[`${MOTHERSHIP_NAME()}.${APEX_DOMAIN()}`]: `http://localhost:${MOTHERSHIP_PORT()}`,
|
||||
[`${APEX_DOMAIN()}`]: `http://localhost:${8080}`,
|
||||
[`${APEX_DOMAIN()}`]: `http://localhost:${5174}`,
|
||||
}
|
||||
const hostnameRoutes = IS_DEV() ? DEV_ROUTES : PROD_ROUTES
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Command } from 'commander'
|
||||
import { logger } from '../../../../common'
|
||||
import { logger } from '../../../common'
|
||||
import { ServeCommand } from './ServeCommand'
|
||||
|
||||
type Options = {
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
LoggerService,
|
||||
MOTHERSHIP_PORT,
|
||||
stringify,
|
||||
} from '../../../../core'
|
||||
} from '../../..'
|
||||
|
||||
export const checkHealth = async () => {
|
||||
const { cpu, drive } = osu
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { execSync } from 'child_process'
|
||||
import { globSync } from 'glob'
|
||||
import { DATA_ROOT } from '../../../../core'
|
||||
import { logger } from '../../../common/Logger'
|
||||
import { DATA_ROOT, logger } from '../../..'
|
||||
|
||||
export const compact = async () => {
|
||||
const { info, error } = logger()
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Command } from 'commander'
|
||||
import { logger } from '../../../../core'
|
||||
import { logger } from '../../..'
|
||||
|
||||
type Options = {
|
||||
debug: boolean
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
MOTHERSHIP_DATA_ROOT,
|
||||
MOTHERSHIP_MIGRATIONS_DIR,
|
||||
MOTHERSHIP_SEMVER,
|
||||
} from '../../../../../core'
|
||||
} from '../../../..'
|
||||
import { GobotService } from '../../../../services/GobotService'
|
||||
|
||||
export async function schema() {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { GobotOptions } from 'gobot'
|
||||
import {
|
||||
APP_URL,
|
||||
DISCORD_ALERT_CHANNEL_URL,
|
||||
DISCORD_HEALTH_CHANNEL_URL,
|
||||
DISCORD_STREAM_CHANNEL_URL,
|
||||
@ -15,7 +16,7 @@ import {
|
||||
TEST_EMAIL,
|
||||
_MOTHERSHIP_APP_ROOT,
|
||||
mkContainerHomePath,
|
||||
} from '../../../../../core'
|
||||
} from '../../../..'
|
||||
import { GobotService } from '../../../../services/GobotService'
|
||||
|
||||
export type MothershipConfig = {}
|
||||
@ -35,6 +36,7 @@ export async function mothership(cfg: MothershipConfig) {
|
||||
DISCORD_HEALTH_CHANNEL_URL: DISCORD_HEALTH_CHANNEL_URL(),
|
||||
DISCORD_ALERT_CHANNEL_URL: DISCORD_ALERT_CHANNEL_URL(),
|
||||
TEST_EMAIL: TEST_EMAIL(),
|
||||
APP_URL: APP_URL(),
|
||||
}
|
||||
dbg(env)
|
||||
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
MOTHERSHIP_HOOKS_DIR,
|
||||
PH_ALLOWED_POCKETBASE_SEMVER,
|
||||
stringify,
|
||||
} from '../../../../../core'
|
||||
} from '../../../..'
|
||||
import { GobotService } from '../../../../services/GobotService'
|
||||
|
||||
function compareSemVer(a: string, b: string): number {
|
||||
|
@ -2,14 +2,14 @@ import { map } from '@s-libs/micro-dash'
|
||||
import Database from 'better-sqlite3'
|
||||
import Bottleneck from 'bottleneck'
|
||||
import { Command, InvalidArgumentError } from 'commander'
|
||||
import { PocketBase, UserFields, logger } from '../../../common'
|
||||
import {
|
||||
MOTHERSHIP_ADMIN_PASSWORD,
|
||||
MOTHERSHIP_ADMIN_USERNAME,
|
||||
MOTHERSHIP_DATA_DB,
|
||||
MOTHERSHIP_URL,
|
||||
TEST_EMAIL,
|
||||
} from '../../../core'
|
||||
} from '../../..'
|
||||
import { PocketBase, UserFields, logger } from '../../../common'
|
||||
|
||||
const TBL_SENT_MESSAGES = `sent_messages`
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Command } from 'commander'
|
||||
import { neverendingPromise } from '../../..'
|
||||
import { logger } from '../../../common'
|
||||
import { neverendingPromise } from '../../../core'
|
||||
import { daemon } from '../EdgeCommand/DaemonCommand/ServeCommand/daemon'
|
||||
import { firewall } from '../FirewallCommand/ServeCommand/firewall/server'
|
||||
import { mothership } from '../MothershipCommand/ServeCommand/mothership'
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
import { program } from 'commander'
|
||||
import EventSource from 'eventsource'
|
||||
import { LogLevelName, gracefulExit, logger } from '../../core'
|
||||
import { LogLevelName, gracefulExit, logger } from '..'
|
||||
import { version } from '../../package.json'
|
||||
import { EdgeCommand } from './commands/EdgeCommand'
|
||||
import { FirewallCommand } from './commands/FirewallCommand'
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { WinstonLoggerService } from '..'
|
||||
import { version } from '../../package.json'
|
||||
import { ioc } from '../common'
|
||||
import { RegisterEnvSettingsService } from '../constants'
|
||||
import { WinstonLoggerService } from '../core/winston'
|
||||
import { GobotService } from '../services/GobotService'
|
||||
|
||||
export const initIoc = async () => {
|
||||
|
@ -12,15 +12,13 @@ import {
|
||||
SettingsHandlerFactory,
|
||||
SettingsService,
|
||||
ioc,
|
||||
} from '../core'
|
||||
import {
|
||||
mkBoolean,
|
||||
mkCsvString,
|
||||
mkNumber,
|
||||
mkPath,
|
||||
mkString,
|
||||
} from './core/Settings'
|
||||
import { settings } from './core/ioc'
|
||||
settings,
|
||||
} from '.'
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
@ -45,7 +43,6 @@ export const _HTTP_PROTOCOL = env
|
||||
.get('HTTP_PROTOCOL')
|
||||
.default('https:')
|
||||
.asString()
|
||||
export const _APP_NAME = env.get('APP_NAME').default('app').asString()
|
||||
export const _MOTHERSHIP_NAME = env
|
||||
.get('MOTHERSHIP_NAME')
|
||||
.default('pockethost-central')
|
||||
@ -78,16 +75,14 @@ const createDevCert = async () => {
|
||||
writeFileSync(join(_SSL_HOME, `${TLS_PFX}.cert`), cert)
|
||||
}
|
||||
|
||||
export const SETTINGS = {
|
||||
export const createSettings = () => ({
|
||||
PH_ALLOWED_POCKETBASE_SEMVER: mkString(`0.22.*`),
|
||||
|
||||
PH_HOME: mkPath(_PH_HOME, { create: true }),
|
||||
PH_PROJECT_ROOT: mkPath(PH_PROJECT_ROOT()),
|
||||
|
||||
HTTP_PROTOCOL: mkString(_HTTP_PROTOCOL),
|
||||
APP_NAME: mkString(_APP_NAME),
|
||||
APP_URL: mkString(`${_HTTP_PROTOCOL}//${_APP_NAME}.${_APEX_DOMAIN}`),
|
||||
BLOG_URL: mkString(`${_HTTP_PROTOCOL}//${_APEX_DOMAIN}`),
|
||||
APP_URL: mkString(`${_HTTP_PROTOCOL}//${_APEX_DOMAIN}`),
|
||||
APEX_DOMAIN: mkString(_APEX_DOMAIN),
|
||||
|
||||
IPCIDR_LIST: mkCsvString([]),
|
||||
@ -142,7 +137,7 @@ export const SETTINGS = {
|
||||
DOCKER_CONTAINER_HOST: mkString(`host.docker.internal`),
|
||||
|
||||
PH_GOBOT_ROOT: mkPath(join(_PH_HOME, 'gobot'), { create: true }),
|
||||
}
|
||||
})
|
||||
|
||||
export type Settings = ReturnType<typeof RegisterEnvSettingsService>
|
||||
export type SettingsDefinition = {
|
||||
@ -150,7 +145,7 @@ export type SettingsDefinition = {
|
||||
}
|
||||
|
||||
export const RegisterEnvSettingsService = () => {
|
||||
const _settings = SettingsService(SETTINGS)
|
||||
const _settings = SettingsService(createSettings())
|
||||
|
||||
ioc('settings', _settings)
|
||||
|
||||
@ -173,11 +168,8 @@ export const DEBUG = () =>
|
||||
env.get(`PH_DEBUG`).default(_IS_DEV.toString()).asBool()
|
||||
|
||||
export const HTTP_PROTOCOL = () => settings().HTTP_PROTOCOL
|
||||
export const APP_URL = () => settings().APP_URL
|
||||
export const APP_NAME = () => settings().APP_NAME
|
||||
export const BLOG_URL = (...path: string[]) =>
|
||||
join(settings().BLOG_URL, ...path)
|
||||
export const DOCS_URL = (...path: string[]) => BLOG_URL(`docs`, ...path)
|
||||
export const APP_URL = (...path: string[]) =>
|
||||
[settings().APP_URL, path.join(`/`)].filter(Boolean).join('/')
|
||||
|
||||
export const APEX_DOMAIN = () => settings().APEX_DOMAIN
|
||||
|
||||
@ -186,13 +178,15 @@ export const DAEMON_PORT = () => settings().DAEMON_PORT
|
||||
export const DAEMON_PB_IDLE_TTL = () => settings().DAEMON_PB_IDLE_TTL
|
||||
|
||||
export const MOTHERSHIP_URL = (...path: string[]) =>
|
||||
join(
|
||||
[
|
||||
env
|
||||
.get('MOTHERSHIP_URL')
|
||||
.default(`${HTTP_PROTOCOL()}://${MOTHERSHIP_NAME()}:${APEX_DOMAIN()}`)
|
||||
.asString(),
|
||||
...path,
|
||||
)
|
||||
path.join('/'),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
|
||||
export const MOTHERSHIP_NAME = () => settings().MOTHERSHIP_NAME
|
||||
export const MOTHERSHIP_ADMIN_USERNAME = () =>
|
||||
@ -264,18 +258,15 @@ export const INSTANCE_DATA_DB = (id: InstanceId) =>
|
||||
join(DATA_ROOT(), id, `pb_data`, `data.db`)
|
||||
export const mkContainerHomePath = (...path: string[]) =>
|
||||
join(`/home/pockethost`, ...path.filter((v) => !!v))
|
||||
export const mkAppUrl = (path = '') => `${APP_URL()}${path}`
|
||||
export const mkBlogUrl = (path = '') => `${BLOG_URL()}${path}`
|
||||
export const mkDocUrl = (path = '') => mkBlogUrl(join('/docs', path))
|
||||
export const DOC_URL = (...path: string[]) => APP_URL('docs', ...path)
|
||||
export const mkInstanceCanonicalHostname = (instance: InstanceFields) =>
|
||||
(instance.cname_active && instance.cname) || `${instance.id}.${APEX_DOMAIN()}`
|
||||
export const mkInstanceHostname = (instance: InstanceFields) =>
|
||||
`${instance.subdomain}.${APEX_DOMAIN()}`
|
||||
[instance.subdomain, APEX_DOMAIN()].filter(Boolean).join('.')
|
||||
export const mkInstanceUrl = (instance: InstanceFields, ...paths: string[]) =>
|
||||
[
|
||||
`${HTTP_PROTOCOL()}//${mkInstanceHostname(instance)}`,
|
||||
paths.length ? join(...paths) : '',
|
||||
].join('')
|
||||
[`${HTTP_PROTOCOL()}//${mkInstanceHostname(instance)}`, paths.join(`/`)]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
export const mkInstanceDataPath = (instanceId: string, ...path: string[]) =>
|
||||
join(settings().DATA_ROOT, instanceId, ...path)
|
||||
|
||||
@ -287,9 +278,6 @@ export const logConstants = () => {
|
||||
PH_PROJECT_ROOT,
|
||||
HTTP_PROTOCOL,
|
||||
APP_URL,
|
||||
APP_NAME,
|
||||
BLOG_URL,
|
||||
DOCS_URL,
|
||||
APEX_DOMAIN,
|
||||
IPCIDR_LIST,
|
||||
DAEMON_PORT,
|
||||
|
19
packages/pockethost/src/core/dir.ts
Normal file
19
packages/pockethost/src/core/dir.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { existsSync, mkdirSync } from 'fs'
|
||||
import { Logger } from '../common'
|
||||
import { mkInstanceDataPath } from '../constants'
|
||||
|
||||
export function ensureInstanceDirectoryStructure(
|
||||
instanceId: string,
|
||||
logger: Logger,
|
||||
) {
|
||||
const { dbg } = logger
|
||||
;['pb_data', 'pb_migrations', 'pb_public', 'logs', 'pb_hooks'].forEach(
|
||||
(dir) => {
|
||||
const path = mkInstanceDataPath(instanceId, dir)
|
||||
if (!existsSync(path)) {
|
||||
dbg(`Creating ${path}`)
|
||||
mkdirSync(path, { recursive: true })
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
export * from '../constants'
|
||||
export * from './asyncExecutionGuard'
|
||||
export * from './dir'
|
||||
export * from './discordAlert'
|
||||
export * from './env'
|
||||
export * from './exit'
|
||||
export * from './internal'
|
||||
export * from './ioc'
|
||||
export * from './process'
|
||||
export * from './Settings'
|
||||
export * from './smartFetch'
|
||||
export * from './tryFetch'
|
||||
export * from './winston'
|
||||
|
@ -9,6 +9,7 @@ const format = winston.format.combine(
|
||||
winston.format.colorize(),
|
||||
winston.format.printf(({ level, message, timestamp, ...meta }) => {
|
||||
const final: string[] = []
|
||||
// @ts-expect-error
|
||||
;[...message, meta].forEach((m: string) => {
|
||||
if (typeof m === 'string' && !!m.match(/\n/)) {
|
||||
final.push(...m.split(/\n/))
|
||||
|
@ -1 +1,2 @@
|
||||
export * from './common'
|
||||
export * from './core'
|
||||
|
@ -1,11 +0,0 @@
|
||||
const express = require('express')
|
||||
|
||||
const app = express()
|
||||
|
||||
app.get(`/apix/status`, (req, res) => {
|
||||
res.json({ status: 'ok' })
|
||||
})
|
||||
|
||||
app.listen(3000, () => {
|
||||
console.log(`Listening on 3000`)
|
||||
})
|
@ -1,657 +0,0 @@
|
||||
{
|
||||
"name": "ph_app",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ph_app",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"express": "^4.18.2"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||
"dependencies": {
|
||||
"mime-types": "~2.1.34",
|
||||
"negotiator": "0.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/array-flatten": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
|
||||
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"content-type": "~1.0.4",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.11.0",
|
||||
"raw-body": "2.5.1",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
|
||||
"integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2",
|
||||
"get-intrinsic": "^1.2.1",
|
||||
"set-function-length": "^1.1.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "5.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/content-type": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/define-data-property": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
|
||||
"integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.2.1",
|
||||
"gopd": "^1.0.1",
|
||||
"has-property-descriptors": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/destroy": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
||||
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
||||
},
|
||||
"node_modules/encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
|
||||
},
|
||||
"node_modules/etag": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.18.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
||||
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.1",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.5.0",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "1.2.0",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "2.0.0",
|
||||
"merge-descriptors": "1.0.1",
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.7",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.11.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"safe-buffer": "5.2.1",
|
||||
"send": "0.18.0",
|
||||
"serve-static": "1.15.0",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"type-is": "~1.6.18",
|
||||
"utils-merge": "1.0.1",
|
||||
"vary": "~1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
|
||||
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"statuses": "2.0.1",
|
||||
"unpipe": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
|
||||
"integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2",
|
||||
"has-proto": "^1.0.1",
|
||||
"has-symbols": "^1.0.3",
|
||||
"hasown": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.1.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-property-descriptors": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
|
||||
"integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
|
||||
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/http-errors": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
||||
"dependencies": {
|
||||
"depd": "2.0.0",
|
||||
"inherits": "2.0.4",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"toidentifier": "1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
|
||||
},
|
||||
"node_modules/methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
|
||||
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||
"dependencies": {
|
||||
"ee-first": "1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
||||
"dependencies": {
|
||||
"forwarded": "0.2.0",
|
||||
"ipaddr.js": "1.9.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
||||
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/send": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
|
||||
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "2.0.0",
|
||||
"mime": "1.6.0",
|
||||
"ms": "2.1.3",
|
||||
"on-finished": "2.4.1",
|
||||
"range-parser": "~1.2.1",
|
||||
"statuses": "2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/send/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=="
|
||||
},
|
||||
"node_modules/serve-static": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
|
||||
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
|
||||
"dependencies": {
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"parseurl": "~1.3.3",
|
||||
"send": "0.18.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/set-function-length": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
|
||||
"integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
|
||||
"dependencies": {
|
||||
"define-data-property": "^1.1.1",
|
||||
"get-intrinsic": "^1.2.1",
|
||||
"gopd": "^1.0.1",
|
||||
"has-property-descriptors": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/setprototypeof": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.0",
|
||||
"get-intrinsic": "^1.0.2",
|
||||
"object-inspect": "^1.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||
"dependencies": {
|
||||
"media-typer": "0.3.0",
|
||||
"mime-types": "~2.1.24"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/utils-merge": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"name": "ph_app",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"express": "^4.18.2"
|
||||
}
|
||||
}
|
@ -1,13 +1,20 @@
|
||||
import * as fs from 'fs'
|
||||
import { appendFile } from 'fs/promises'
|
||||
import { Tail } from 'tail'
|
||||
import { LoggerService, mkInstanceDataPath, stringify } from '../../../core'
|
||||
import {
|
||||
ensureInstanceDirectoryStructure,
|
||||
LoggerService,
|
||||
mkInstanceDataPath,
|
||||
stringify,
|
||||
} from '../..'
|
||||
|
||||
type UnsubFunc = () => void
|
||||
|
||||
export type InstanceLoggerApi = {
|
||||
export type InstanceLogWriterApi = {
|
||||
info: (msg: string) => void
|
||||
error: (msg: string) => void
|
||||
}
|
||||
|
||||
export type InstanceLogReaderApi = {
|
||||
tail: (linesBack: number, data: (line: LogEntry) => void) => UnsubFunc
|
||||
shutdown: () => void
|
||||
}
|
||||
@ -18,26 +25,11 @@ export type LogEntry = {
|
||||
time: string
|
||||
}
|
||||
|
||||
export type InstanceLoggerOptions = {}
|
||||
export function InstanceLogWriter(instanceId: string, target: string) {
|
||||
const logger = LoggerService().create(instanceId).breadcrumb({ target })
|
||||
const { dbg, info, error, warn } = logger
|
||||
|
||||
const loggers: {
|
||||
[key: string]: InstanceLoggerApi
|
||||
} = {}
|
||||
|
||||
export function InstanceLogger(
|
||||
instanceId: string,
|
||||
target: string,
|
||||
options: Partial<InstanceLoggerOptions> = {},
|
||||
) {
|
||||
const { dbg, info, error, warn } = LoggerService()
|
||||
.create(instanceId)
|
||||
.breadcrumb({ target })
|
||||
|
||||
const logDirectory = mkInstanceDataPath(instanceId, `logs`)
|
||||
if (!fs.existsSync(logDirectory)) {
|
||||
dbg(`Creating ${logDirectory}`)
|
||||
fs.mkdirSync(logDirectory, { recursive: true })
|
||||
}
|
||||
ensureInstanceDirectoryStructure(instanceId, logger)
|
||||
|
||||
const logFile = mkInstanceDataPath(instanceId, `logs`, `${target}.log`)
|
||||
|
||||
@ -61,6 +53,18 @@ export function InstanceLogger(
|
||||
error(msg)
|
||||
appendLogEntry(msg, 'stderr')
|
||||
},
|
||||
}
|
||||
|
||||
return api
|
||||
}
|
||||
|
||||
export function InstanceLogReader(instanceId: string, target: string) {
|
||||
const logger = LoggerService().create(instanceId).breadcrumb({ target })
|
||||
const { dbg, info, error, warn } = logger
|
||||
|
||||
ensureInstanceDirectoryStructure(instanceId, logger)
|
||||
|
||||
const api = {
|
||||
tail: (linesBack: number, data: (line: LogEntry) => void): UnsubFunc => {
|
||||
const logFile = mkInstanceDataPath(instanceId, `logs`, `${target}.log`)
|
||||
|
||||
|
@ -4,8 +4,10 @@ import { globSync } from 'glob'
|
||||
import { basename, join } from 'path'
|
||||
import { AsyncReturnType } from 'type-fest'
|
||||
import {
|
||||
APP_URL,
|
||||
ClientResponseError,
|
||||
DAEMON_PB_IDLE_TTL,
|
||||
DOC_URL,
|
||||
EDGE_APEX_DOMAIN,
|
||||
INSTANCE_APP_HOOK_DIR,
|
||||
INSTANCE_APP_MIGRATIONS_DIR,
|
||||
@ -15,17 +17,15 @@ import {
|
||||
LoggerService,
|
||||
SingletonBaseConfig,
|
||||
asyncExitHook,
|
||||
mkAppUrl,
|
||||
mkContainerHomePath,
|
||||
mkDocUrl,
|
||||
mkInstanceUrl,
|
||||
mkSingleton,
|
||||
now,
|
||||
stringify,
|
||||
tryFetch,
|
||||
} from '../../../core'
|
||||
} from '../..'
|
||||
import {
|
||||
InstanceLogger,
|
||||
InstanceLogWriter,
|
||||
MothershipAdminClientService,
|
||||
PocketbaseService,
|
||||
SpawnConfig,
|
||||
@ -71,7 +71,7 @@ export const instanceService = mkSingleton(
|
||||
`${subdomain}:${id}:${version}`,
|
||||
)
|
||||
const { dbg, warn, error, info, trace } = systemInstanceLogger
|
||||
const userInstanceLogger = InstanceLogger(instance.id, `exec`)
|
||||
const userInstanceLogger = InstanceLogWriter(instance.id, `exec`)
|
||||
|
||||
shutdownManager.push(() => {
|
||||
dbg(`Shutting down`)
|
||||
@ -332,7 +332,7 @@ export const instanceService = mkSingleton(
|
||||
dbg(`Checking for maintenance mode`)
|
||||
if (instance.maintenance) {
|
||||
throw new Error(
|
||||
`This instance is powered off. See ${mkDocUrl(
|
||||
`This instance is powered off. See ${DOC_URL(
|
||||
`power`,
|
||||
)} for more information.`,
|
||||
)
|
||||
@ -343,7 +343,7 @@ export const instanceService = mkSingleton(
|
||||
*/
|
||||
dbg(`Checking for verified account`)
|
||||
if (!owner.verified) {
|
||||
throw new Error(`Log in at ${mkAppUrl()} to verify your account.`)
|
||||
throw new Error(`Log in at ${APP_URL()} to verify your account.`)
|
||||
}
|
||||
|
||||
const api = await (instanceApis[instance.id] =
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
PocketBase,
|
||||
UserFields,
|
||||
UserId,
|
||||
} from '../../../core'
|
||||
} from '../..'
|
||||
|
||||
export const mkInstanceCache = (client: PocketBase) => {
|
||||
const { dbg } = LoggerService().create(`InstanceCache`)
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
InstanceId,
|
||||
InstanceStatus,
|
||||
WithUser,
|
||||
} from '../../../core'
|
||||
} from '../..'
|
||||
|
||||
export type InstanceApi = ReturnType<typeof createInstanceMixin>
|
||||
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
RestMethods,
|
||||
createRestHelper,
|
||||
stringify,
|
||||
} from '../../../core'
|
||||
} from '../..'
|
||||
import { createInstanceMixin } from './InstanceMIxin'
|
||||
|
||||
export type PocketbaseClientApi = ReturnType<typeof createAdminPbClient>
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
PocketBase,
|
||||
mergeConfig,
|
||||
mkSingleton,
|
||||
} from '../../../core'
|
||||
} from '../..'
|
||||
import { createAdminPbClient } from './createAdminPbClient'
|
||||
|
||||
export type ClientServiceConfig = {
|
||||
|
@ -15,9 +15,9 @@ import {
|
||||
mkInstanceDataPath,
|
||||
mkInternalUrl,
|
||||
mkSingleton,
|
||||
} from '../../../core'
|
||||
} from '../..'
|
||||
import { GobotService } from '../GobotService'
|
||||
import { InstanceLogger } from '../InstanceLoggerService'
|
||||
import { InstanceLogWriter } from '../InstanceLoggerService'
|
||||
|
||||
export type Env = { [_: string]: string }
|
||||
export type SpawnConfig = {
|
||||
@ -85,7 +85,7 @@ export const createPocketbaseService = async (
|
||||
} = _cfg
|
||||
|
||||
logger.breadcrumb({ subdomain, instanceId })
|
||||
const iLogger = InstanceLogger(instanceId, 'exec')
|
||||
const iLogger = InstanceLogWriter(instanceId, 'exec')
|
||||
|
||||
const _version = version || maxVersion // If _version is blank, we use the max version available
|
||||
const realVersion = await bot.maxSatisfyingVersion(_version)
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
asyncExitHook,
|
||||
mkSingleton,
|
||||
seqid,
|
||||
} from '../../core'
|
||||
} from '..'
|
||||
|
||||
export type ProxyServiceApi = AsyncReturnType<typeof proxyService>
|
||||
|
||||
@ -30,79 +30,83 @@ export type ProxyMiddleware = (
|
||||
export type ProxyServiceConfig = SingletonBaseConfig & {
|
||||
coreInternalUrl: string
|
||||
}
|
||||
export const proxyService = mkSingleton(async (config: ProxyServiceConfig) => {
|
||||
const _proxyLogger = LoggerService().create('ProxyService')
|
||||
const { dbg, error, info, trace, warn } = _proxyLogger
|
||||
export const proxyService = mkSingleton(
|
||||
async (
|
||||
config: ProxyServiceConfig,
|
||||
): Promise<{ use: ReturnType<typeof express>['use'] }> => {
|
||||
const _proxyLogger = LoggerService().create('ProxyService')
|
||||
const { dbg, error, info, trace, warn } = _proxyLogger
|
||||
|
||||
const { coreInternalUrl } = config
|
||||
const { coreInternalUrl } = config
|
||||
|
||||
const proxy = httpProxy.createProxyServer({})
|
||||
proxy.on('error', (err, req, res, target) => {
|
||||
warn(`Proxy error ${err} on ${req.url} (${req.headers.host})`)
|
||||
})
|
||||
const proxy = httpProxy.createProxyServer({})
|
||||
proxy.on('error', (err, req, res, target) => {
|
||||
warn(`Proxy error ${err} on ${req.url} (${req.headers.host})`)
|
||||
})
|
||||
|
||||
const server = express()
|
||||
const server = express()
|
||||
|
||||
server.use(cors())
|
||||
server.use(cors())
|
||||
|
||||
server.get('/_api/health', (req, res, next) => {
|
||||
res.json({ status: 'ok' })
|
||||
res.end
|
||||
})
|
||||
server.get('/_api/health', (req, res, next) => {
|
||||
res.json({ status: 'ok' })
|
||||
res.end
|
||||
})
|
||||
|
||||
// Default locals
|
||||
server.use((req, res, next) => {
|
||||
const host = req.headers.host
|
||||
res.locals.requestId = seqid()
|
||||
res.locals.host = host
|
||||
res.locals.coreInternalUrl = coreInternalUrl
|
||||
next()
|
||||
})
|
||||
// Default locals
|
||||
server.use((req, res, next) => {
|
||||
const host = req.headers.host
|
||||
res.locals.requestId = seqid()
|
||||
res.locals.host = host
|
||||
res.locals.coreInternalUrl = coreInternalUrl
|
||||
next()
|
||||
})
|
||||
|
||||
// Cloudflare signature
|
||||
server.use((req, res, next) => {
|
||||
const url = new URL(`https://${res.locals.host}${req.url}`)
|
||||
const country = (req.headers['cf-ipcountry'] as string) || '<ct>'
|
||||
const ip = (req.headers['x-forwarded-for'] as string) || '<ip>'
|
||||
const method = req.method || '<m>'
|
||||
const sig = [
|
||||
res.locals.requestId,
|
||||
method.padStart(10),
|
||||
country.padStart(5),
|
||||
ip.padEnd(45),
|
||||
url.toString(),
|
||||
].join(' ')
|
||||
res.locals.sig = sig
|
||||
next()
|
||||
})
|
||||
// Cloudflare signature
|
||||
server.use((req, res, next) => {
|
||||
const url = new URL(`https://${res.locals.host}${req.url}`)
|
||||
const country = (req.headers['cf-ipcountry'] as string) || '<ct>'
|
||||
const ip = (req.headers['x-forwarded-for'] as string) || '<ip>'
|
||||
const method = req.method || '<m>'
|
||||
const sig = [
|
||||
res.locals.requestId,
|
||||
method.padStart(10),
|
||||
country.padStart(5),
|
||||
ip.padEnd(45),
|
||||
url.toString(),
|
||||
].join(' ')
|
||||
res.locals.sig = sig
|
||||
next()
|
||||
})
|
||||
|
||||
server.use((req, res, next) => {
|
||||
res.locals.proxy = proxy
|
||||
next()
|
||||
})
|
||||
server.use((req, res, next) => {
|
||||
res.locals.proxy = proxy
|
||||
next()
|
||||
})
|
||||
|
||||
// Request logging
|
||||
server.use((req, res, next) => {
|
||||
if (!res.locals.host) {
|
||||
throw new Error(`Host not found`)
|
||||
}
|
||||
next()
|
||||
})
|
||||
// Request logging
|
||||
server.use((req, res, next) => {
|
||||
if (!res.locals.host) {
|
||||
throw new Error(`Host not found`)
|
||||
}
|
||||
next()
|
||||
})
|
||||
|
||||
server.use((req, res, next) => {
|
||||
info(`Incoming request ${res.locals.sig}`)
|
||||
next()
|
||||
})
|
||||
server.use((req, res, next) => {
|
||||
info(`Incoming request ${res.locals.sig}`)
|
||||
next()
|
||||
})
|
||||
|
||||
server.listen(DAEMON_PORT(), () => {
|
||||
info(`daemon listening on port ${DAEMON_PORT()}`)
|
||||
})
|
||||
server.listen(DAEMON_PORT(), () => {
|
||||
info(`daemon listening on port ${DAEMON_PORT()}`)
|
||||
})
|
||||
|
||||
asyncExitHook(async () => {
|
||||
info(`Shutting down proxy server`)
|
||||
})
|
||||
asyncExitHook(async () => {
|
||||
info(`Shutting down proxy server`)
|
||||
})
|
||||
|
||||
const use = server.use.bind(server)
|
||||
const use = server.use.bind(server)
|
||||
|
||||
return { use }
|
||||
})
|
||||
return { use }
|
||||
},
|
||||
)
|
||||
|
@ -7,8 +7,8 @@ import {
|
||||
PocketBase,
|
||||
SingletonBaseConfig,
|
||||
stringify,
|
||||
} from '../../core'
|
||||
import { InstanceLogger } from './InstanceLoggerService'
|
||||
} from '..'
|
||||
import { InstanceLogReader } from './InstanceLoggerService'
|
||||
import { proxyService } from './ProxyService'
|
||||
|
||||
export type RealtimeLogConfig = SingletonBaseConfig & {}
|
||||
@ -69,7 +69,7 @@ export const realtimeLog = mkSingleton(async (config: RealtimeLogConfig) => {
|
||||
dbg(`Instance is `, instance)
|
||||
|
||||
/** Get a database connection */
|
||||
const instanceLogger = InstanceLogger(instance.id, `exec`)
|
||||
const instanceLogger = InstanceLogReader(instance.id, `exec`)
|
||||
|
||||
/** Start the stream */
|
||||
res.writeHead(200, {
|
||||
|
1189
pnpm-lock.yaml
generated
1189
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user