@onesub/cli 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +115 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
- package/templates/.env.example +22 -0
- package/templates/README.md +43 -0
- package/templates/docker-compose.yml +41 -0
- package/templates/package.json +25 -0
- package/templates/server.ts +45 -0
- package/templates/tsconfig.json +13 -0
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @onesub/cli
|
|
2
|
+
|
|
3
|
+
Scaffolds a ready-to-run [onesub](https://github.com/jeonghwanko/onesub) server project.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx @onesub/cli init my-onesub-server
|
|
7
|
+
cd my-onesub-server
|
|
8
|
+
cp .env.example .env
|
|
9
|
+
npm install
|
|
10
|
+
npm run dev
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or full stack (Postgres + server) in one command:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
docker compose up
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## What it generates
|
|
20
|
+
|
|
21
|
+
| File | Purpose |
|
|
22
|
+
|------|---------|
|
|
23
|
+
| `server.ts` | Express app with `createOneSubMiddleware()` wired up |
|
|
24
|
+
| `.env.example` | Apple / Google / Postgres / admin placeholders |
|
|
25
|
+
| `docker-compose.yml` | Postgres + server, schema auto-initialized from `@onesub/server/sql/schema.sql` |
|
|
26
|
+
| `package.json`, `tsconfig.json`, `.gitignore`, `README.md` | Standard project shell |
|
|
27
|
+
|
|
28
|
+
That's it. No prompts, no interactive questionnaire — same scaffold every time, tweak it yourself afterward.
|
|
29
|
+
|
|
30
|
+
## Commands
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
onesub init [directory] Scaffold into <directory> (default: .)
|
|
34
|
+
onesub --help Show help
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
MIT.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* onesub CLI — scaffolds a starter server (+ optional Expo app) in a new or
|
|
4
|
+
* existing directory.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx @onesub/cli init [directory]
|
|
8
|
+
* npx @onesub/cli --help
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* onesub CLI — scaffolds a starter server (+ optional Expo app) in a new or
|
|
4
|
+
* existing directory.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx @onesub/cli init [directory]
|
|
8
|
+
* npx @onesub/cli --help
|
|
9
|
+
*/
|
|
10
|
+
import { mkdir, writeFile, readdir, readFile } from 'node:fs/promises';
|
|
11
|
+
import { existsSync } from 'node:fs';
|
|
12
|
+
import { join, resolve, dirname } from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
// dist/index.js → packages/cli/templates
|
|
16
|
+
const TEMPLATES_DIR = resolve(here, '..', 'templates');
|
|
17
|
+
const HELP = `onesub — scaffold a receipt-validation server and paywall app
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
onesub init [directory] Create a new onesub project in <directory> (default: .)
|
|
21
|
+
onesub --help Show this help
|
|
22
|
+
|
|
23
|
+
What 'init' creates:
|
|
24
|
+
server.ts Express server with createOneSubMiddleware() wired up
|
|
25
|
+
.env.example Apple + Google credential placeholders
|
|
26
|
+
package.json With @onesub/server as a dependency
|
|
27
|
+
docker-compose.yml Postgres + server, schema auto-initialized
|
|
28
|
+
README.md Next-steps guide
|
|
29
|
+
|
|
30
|
+
After init:
|
|
31
|
+
cd <directory>
|
|
32
|
+
cp .env.example .env # fill in your credentials
|
|
33
|
+
npm install
|
|
34
|
+
npm start # http://localhost:4100
|
|
35
|
+
`;
|
|
36
|
+
function parseArgs(argv) {
|
|
37
|
+
const args = argv.slice(2);
|
|
38
|
+
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
39
|
+
return { cmd: 'help', target: '.' };
|
|
40
|
+
}
|
|
41
|
+
if (args[0] !== 'init') {
|
|
42
|
+
return { cmd: 'unknown', target: args[0] ?? '' };
|
|
43
|
+
}
|
|
44
|
+
return { cmd: 'init', target: args[1] ?? '.' };
|
|
45
|
+
}
|
|
46
|
+
async function isEmpty(dir) {
|
|
47
|
+
if (!existsSync(dir))
|
|
48
|
+
return true;
|
|
49
|
+
const entries = await readdir(dir);
|
|
50
|
+
return entries.length === 0;
|
|
51
|
+
}
|
|
52
|
+
async function copyTemplate(relPath, destDir) {
|
|
53
|
+
const src = join(TEMPLATES_DIR, relPath);
|
|
54
|
+
const dst = join(destDir, relPath);
|
|
55
|
+
await mkdir(dirname(dst), { recursive: true });
|
|
56
|
+
const content = await readFile(src, 'utf8');
|
|
57
|
+
await writeFile(dst, content, 'utf8');
|
|
58
|
+
console.log(` ${relPath}`);
|
|
59
|
+
}
|
|
60
|
+
async function init(target) {
|
|
61
|
+
const dir = resolve(target);
|
|
62
|
+
const empty = await isEmpty(dir);
|
|
63
|
+
if (!empty) {
|
|
64
|
+
console.error(`error: ${dir} is not empty. Pick a new directory or remove existing files.`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
await mkdir(dir, { recursive: true });
|
|
68
|
+
console.log(`\nScaffolding onesub server into ${dir}\n`);
|
|
69
|
+
const files = [
|
|
70
|
+
'package.json',
|
|
71
|
+
'server.ts',
|
|
72
|
+
'tsconfig.json',
|
|
73
|
+
'.env.example',
|
|
74
|
+
'docker-compose.yml',
|
|
75
|
+
'README.md',
|
|
76
|
+
'.gitignore',
|
|
77
|
+
];
|
|
78
|
+
for (const f of files) {
|
|
79
|
+
await copyTemplate(f, dir);
|
|
80
|
+
}
|
|
81
|
+
console.log(`
|
|
82
|
+
Done. Next steps:
|
|
83
|
+
|
|
84
|
+
cd ${target === '.' ? '.' : target}
|
|
85
|
+
cp .env.example .env # fill in Apple / Google credentials
|
|
86
|
+
npm install
|
|
87
|
+
npm run dev # http://localhost:4100
|
|
88
|
+
|
|
89
|
+
Or with Docker (Postgres + server):
|
|
90
|
+
|
|
91
|
+
docker compose up
|
|
92
|
+
|
|
93
|
+
Docs: https://github.com/jeonghwanko/onesub
|
|
94
|
+
`);
|
|
95
|
+
}
|
|
96
|
+
async function main() {
|
|
97
|
+
const { cmd, target } = parseArgs(process.argv);
|
|
98
|
+
switch (cmd) {
|
|
99
|
+
case 'help':
|
|
100
|
+
console.log(HELP);
|
|
101
|
+
return;
|
|
102
|
+
case 'init':
|
|
103
|
+
await init(target);
|
|
104
|
+
return;
|
|
105
|
+
default:
|
|
106
|
+
console.error(`Unknown command: ${target}\n`);
|
|
107
|
+
console.error(HELP);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
main().catch((err) => {
|
|
112
|
+
console.error('onesub cli failed:', err);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
});
|
|
115
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@onesub/cli",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "CLI scaffolding for onesub — `npx onesub init` bootstraps a server + Expo app.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"onesub": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"templates"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"dev": "tsc --watch",
|
|
17
|
+
"start": "node dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"onesub",
|
|
21
|
+
"cli",
|
|
22
|
+
"init",
|
|
23
|
+
"scaffolding",
|
|
24
|
+
"react-native-iap",
|
|
25
|
+
"in-app-purchase"
|
|
26
|
+
],
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/jeonghwanko/onesub.git",
|
|
30
|
+
"directory": "packages/cli"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/jeonghwanko/onesub",
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@onesub/shared": "0.3.2"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^20.17.0",
|
|
42
|
+
"typescript": "^5.7.0"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=20"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# ── Apple App Store ──────────────────────────────────────
|
|
2
|
+
# From App Store Connect → Users and Access → Integrations → Keys
|
|
3
|
+
APPLE_BUNDLE_ID=com.yourapp.id
|
|
4
|
+
APPLE_SHARED_SECRET=your_shared_secret
|
|
5
|
+
|
|
6
|
+
# ── Google Play ─────────────────────────────────────────
|
|
7
|
+
# Service account JSON from Google Cloud Console (single-line)
|
|
8
|
+
GOOGLE_PACKAGE_NAME=com.yourapp.id
|
|
9
|
+
GOOGLE_SERVICE_ACCOUNT_KEY={"type":"service_account","project_id":"..."}
|
|
10
|
+
# Optional: push endpoint URL for Pub/Sub JWT verification
|
|
11
|
+
# GOOGLE_PUSH_AUDIENCE=https://api.yourapp.com/onesub/webhook/google
|
|
12
|
+
|
|
13
|
+
# ── Database ────────────────────────────────────────────
|
|
14
|
+
# Uncomment for Postgres; leave unset for in-memory (dev only)
|
|
15
|
+
# DATABASE_URL=postgresql://onesub:onesub@localhost:5432/onesub
|
|
16
|
+
|
|
17
|
+
# ── Admin (optional) ────────────────────────────────────
|
|
18
|
+
# Enables /onesub/purchase/admin/* routes
|
|
19
|
+
# ADMIN_SECRET=change-me
|
|
20
|
+
|
|
21
|
+
# ── Server ──────────────────────────────────────────────
|
|
22
|
+
PORT=4100
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# my-onesub-server
|
|
2
|
+
|
|
3
|
+
Scaffolded by [`npx @onesub/cli init`](https://github.com/jeonghwanko/onesub).
|
|
4
|
+
|
|
5
|
+
## Run
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cp .env.example .env # fill in Apple / Google credentials
|
|
9
|
+
npm install
|
|
10
|
+
npm run dev # http://localhost:4100
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or the full stack (Postgres + server) with Docker:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
docker compose up
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## What you got
|
|
20
|
+
|
|
21
|
+
| File | What |
|
|
22
|
+
|------|------|
|
|
23
|
+
| `server.ts` | Express server with `createOneSubMiddleware()` wired up |
|
|
24
|
+
| `.env.example` | Apple / Google / DB placeholders |
|
|
25
|
+
| `docker-compose.yml` | Postgres + server, schema auto-initialized from `@onesub/server/sql/schema.sql` |
|
|
26
|
+
|
|
27
|
+
## Endpoints mounted
|
|
28
|
+
|
|
29
|
+
- `POST /onesub/validate` — subscription receipt
|
|
30
|
+
- `GET /onesub/status?userId=` — subscription state
|
|
31
|
+
- `POST /onesub/webhook/apple` — App Store Server Notifications V2
|
|
32
|
+
- `POST /onesub/webhook/google` — Google Play RTDN
|
|
33
|
+
- `POST /onesub/purchase/validate` — one-time purchase (consumable / non-consumable)
|
|
34
|
+
- `GET /onesub/purchase/status?userId=` — list purchases
|
|
35
|
+
- `POST /onesub/purchase/admin/*` — admin routes (if `ADMIN_SECRET` is set)
|
|
36
|
+
|
|
37
|
+
## Next
|
|
38
|
+
|
|
39
|
+
- Add your own auth middleware before `createOneSubMiddleware(...)` if `userId` should not be client-trusted
|
|
40
|
+
- Install [`@jeonghwanko/onesub-sdk`](https://www.npmjs.com/package/@jeonghwanko/onesub-sdk) in your React Native app for the `useOneSub()` hook
|
|
41
|
+
- Read [onesub docs](https://github.com/jeonghwanko/onesub)
|
|
42
|
+
|
|
43
|
+
MIT.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
services:
|
|
2
|
+
db:
|
|
3
|
+
image: postgres:16-alpine
|
|
4
|
+
restart: unless-stopped
|
|
5
|
+
environment:
|
|
6
|
+
POSTGRES_USER: onesub
|
|
7
|
+
POSTGRES_PASSWORD: onesub
|
|
8
|
+
POSTGRES_DB: onesub
|
|
9
|
+
ports:
|
|
10
|
+
- '5432:5432'
|
|
11
|
+
volumes:
|
|
12
|
+
- onesub-pgdata:/var/lib/postgresql/data
|
|
13
|
+
# Canonical schema shipped with @onesub/server at node_modules/@onesub/server/sql/schema.sql
|
|
14
|
+
- ./node_modules/@onesub/server/sql/schema.sql:/docker-entrypoint-initdb.d/10-onesub-schema.sql:ro
|
|
15
|
+
healthcheck:
|
|
16
|
+
test: ['CMD-SHELL', 'pg_isready -U onesub -d onesub']
|
|
17
|
+
interval: 5s
|
|
18
|
+
timeout: 3s
|
|
19
|
+
retries: 10
|
|
20
|
+
|
|
21
|
+
server:
|
|
22
|
+
image: node:20-alpine
|
|
23
|
+
depends_on:
|
|
24
|
+
db:
|
|
25
|
+
condition: service_healthy
|
|
26
|
+
working_dir: /app
|
|
27
|
+
command: sh -c "npm install --no-audit --no-fund && npm run dev"
|
|
28
|
+
environment:
|
|
29
|
+
PORT: '4100'
|
|
30
|
+
DATABASE_URL: postgresql://onesub:onesub@db:5432/onesub
|
|
31
|
+
APPLE_BUNDLE_ID: ${APPLE_BUNDLE_ID:-}
|
|
32
|
+
APPLE_SHARED_SECRET: ${APPLE_SHARED_SECRET:-}
|
|
33
|
+
GOOGLE_PACKAGE_NAME: ${GOOGLE_PACKAGE_NAME:-}
|
|
34
|
+
GOOGLE_SERVICE_ACCOUNT_KEY: ${GOOGLE_SERVICE_ACCOUNT_KEY:-}
|
|
35
|
+
ports:
|
|
36
|
+
- '4100:4100'
|
|
37
|
+
volumes:
|
|
38
|
+
- .:/app
|
|
39
|
+
|
|
40
|
+
volumes:
|
|
41
|
+
onesub-pgdata:
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-onesub-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "tsx server.ts",
|
|
8
|
+
"start": "tsx server.ts"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@onesub/server": "^0.6.0",
|
|
12
|
+
"dotenv": "^16.4.5",
|
|
13
|
+
"express": "^4.19.2",
|
|
14
|
+
"pg": "^8.13.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/express": "^4.17.21",
|
|
18
|
+
"@types/node": "^20.17.0",
|
|
19
|
+
"tsx": "^4.19.0",
|
|
20
|
+
"typescript": "^5.7.0"
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=20"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import express from 'express';
|
|
3
|
+
import {
|
|
4
|
+
createOneSubMiddleware,
|
|
5
|
+
InMemorySubscriptionStore,
|
|
6
|
+
InMemoryPurchaseStore,
|
|
7
|
+
PostgresSubscriptionStore,
|
|
8
|
+
PostgresPurchaseStore,
|
|
9
|
+
} from '@onesub/server';
|
|
10
|
+
|
|
11
|
+
const app = express();
|
|
12
|
+
const port = parseInt(process.env.PORT ?? '4100', 10);
|
|
13
|
+
|
|
14
|
+
// Pick stores based on whether DATABASE_URL is set.
|
|
15
|
+
const dbUrl = process.env.DATABASE_URL;
|
|
16
|
+
const store = dbUrl ? new PostgresSubscriptionStore(dbUrl) : new InMemorySubscriptionStore();
|
|
17
|
+
const purchaseStore = dbUrl ? new PostgresPurchaseStore(dbUrl) : new InMemoryPurchaseStore();
|
|
18
|
+
|
|
19
|
+
if (store instanceof PostgresSubscriptionStore) await store.initSchema();
|
|
20
|
+
if (purchaseStore instanceof PostgresPurchaseStore) await purchaseStore.initSchema();
|
|
21
|
+
|
|
22
|
+
app.use(
|
|
23
|
+
createOneSubMiddleware({
|
|
24
|
+
apple: process.env.APPLE_BUNDLE_ID
|
|
25
|
+
? { bundleId: process.env.APPLE_BUNDLE_ID, sharedSecret: process.env.APPLE_SHARED_SECRET }
|
|
26
|
+
: undefined,
|
|
27
|
+
google: process.env.GOOGLE_PACKAGE_NAME
|
|
28
|
+
? {
|
|
29
|
+
packageName: process.env.GOOGLE_PACKAGE_NAME,
|
|
30
|
+
serviceAccountKey: process.env.GOOGLE_SERVICE_ACCOUNT_KEY,
|
|
31
|
+
pushAudience: process.env.GOOGLE_PUSH_AUDIENCE,
|
|
32
|
+
}
|
|
33
|
+
: undefined,
|
|
34
|
+
database: { url: dbUrl ?? '' },
|
|
35
|
+
store,
|
|
36
|
+
purchaseStore,
|
|
37
|
+
adminSecret: process.env.ADMIN_SECRET,
|
|
38
|
+
}),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
app.get('/health', (_req, res) => res.json({ ok: true }));
|
|
42
|
+
|
|
43
|
+
app.listen(port, () => {
|
|
44
|
+
console.log(`[onesub] listening on http://localhost:${port}`);
|
|
45
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"resolveJsonModule": true
|
|
11
|
+
},
|
|
12
|
+
"include": ["server.ts"]
|
|
13
|
+
}
|