Compare commits
No commits in common. "266e9a892d0e4db59885a43c005ee874dff69308" and "5cfefc1ca84a95c434c4b441793f11652de8ef35" have entirely different histories.
266e9a892d
...
5cfefc1ca8
@ -23,7 +23,7 @@ export async function getCost ({ subName, parentId, uploadIds, boost = 0, bio },
|
|||||||
|
|
||||||
// sub allows freebies (or is a bio or a comment), cost is less than baseCost, not anon,
|
// sub allows freebies (or is a bio or a comment), cost is less than baseCost, not anon,
|
||||||
// cost must be greater than user's balance, and user has not disabled freebies
|
// cost must be greater than user's balance, and user has not disabled freebies
|
||||||
const freebie = (parentId || bio) && cost <= baseCost && !!me &&
|
const freebie = (parentId || bio || sub?.allowFreebies) && cost <= baseCost && !!me &&
|
||||||
cost > me?.msats && !me?.disableFreebies
|
cost > me?.msats && !me?.disableFreebies
|
||||||
|
|
||||||
return freebie ? BigInt(0) : BigInt(cost)
|
return freebie ? BigInt(0) : BigInt(cost)
|
||||||
|
@ -23,19 +23,19 @@ import performPaidAction from '../paidAction'
|
|||||||
|
|
||||||
function commentsOrderByClause (me, models, sort) {
|
function commentsOrderByClause (me, models, sort) {
|
||||||
if (sort === 'recent') {
|
if (sort === 'recent') {
|
||||||
return 'ORDER BY ("Item"."deletedAt" IS NULL) DESC, ("Item".cost > 0 OR "Item"."weightedVotes" - "Item"."weightedDownVotes" > 0) DESC, "Item".created_at DESC, "Item".id DESC'
|
return 'ORDER BY "Item".created_at DESC, "Item".id DESC'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (me && sort === 'hot') {
|
if (me && sort === 'hot') {
|
||||||
return `ORDER BY ("Item"."deletedAt" IS NULL) DESC, COALESCE(
|
return `ORDER BY COALESCE(
|
||||||
personal_hot_score,
|
personal_hot_score,
|
||||||
${orderByNumerator(models, 0)}/POWER(GREATEST(3, EXTRACT(EPOCH FROM (now_utc() - "Item".created_at))/3600), 1.3)) DESC NULLS LAST,
|
${orderByNumerator(models, 0)}/POWER(GREATEST(3, EXTRACT(EPOCH FROM (now_utc() - "Item".created_at))/3600), 1.3)) DESC NULLS LAST,
|
||||||
"Item".msats DESC, ("Item".cost > 0) DESC, "Item".id DESC`
|
"Item".msats DESC, ("Item".cost > 0) DESC, "Item".id DESC`
|
||||||
} else {
|
} else {
|
||||||
if (sort === 'top') {
|
if (sort === 'top') {
|
||||||
return `ORDER BY ("Item"."deletedAt" IS NULL) DESC, ${orderByNumerator(models, 0)} DESC NULLS LAST, "Item".msats DESC, ("Item".cost > 0) DESC, "Item".id DESC`
|
return `ORDER BY ${orderByNumerator(models, 0)} DESC NULLS LAST, "Item".msats DESC, ("Item".cost > 0) DESC, "Item".id DESC`
|
||||||
} else {
|
} else {
|
||||||
return `ORDER BY ("Item"."deletedAt" IS NULL) DESC, ${orderByNumerator(models, 0)}/POWER(GREATEST(3, EXTRACT(EPOCH FROM (now_utc() - "Item".created_at))/3600), 1.3) DESC NULLS LAST, "Item".msats DESC, ("Item".cost > 0) DESC, "Item".id DESC`
|
return `ORDER BY ${orderByNumerator(models, 0)}/POWER(GREATEST(3, EXTRACT(EPOCH FROM (now_utc() - "Item".created_at))/3600), 1.3) DESC NULLS LAST, "Item".msats DESC, ("Item".cost > 0) DESC, "Item".id DESC`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1290,7 +1290,11 @@ export const updateItem = async (parent, { sub: subName, forward, ...item }, { m
|
|||||||
const differentSub = subName && old.subName !== subName
|
const differentSub = subName && old.subName !== subName
|
||||||
if (differentSub) {
|
if (differentSub) {
|
||||||
const sub = await models.sub.findUnique({ where: { name: subName } })
|
const sub = await models.sub.findUnique({ where: { name: subName } })
|
||||||
if (sub.baseCost > old.sub.baseCost) {
|
if (old.cost === 0) {
|
||||||
|
if (!sub.allowFreebies) {
|
||||||
|
throw new GraphQLError(`~${subName} does not allow freebies`, { extensions: { code: 'BAD_INPUT' } })
|
||||||
|
}
|
||||||
|
} else if (sub.baseCost > old.sub.baseCost) {
|
||||||
throw new GraphQLError('cannot change to a more expensive sub', { extensions: { code: 'BAD_INPUT' } })
|
throw new GraphQLError('cannot change to a more expensive sub', { extensions: { code: 'BAD_INPUT' } })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ export default gql`
|
|||||||
|
|
||||||
extend type Mutation {
|
extend type Mutation {
|
||||||
upsertSub(oldName: String, name: String!, desc: String, baseCost: Int!,
|
upsertSub(oldName: String, name: String!, desc: String, baseCost: Int!,
|
||||||
postTypes: [String!]!,
|
postTypes: [String!]!, allowFreebies: Boolean!,
|
||||||
billingType: String!, billingAutoRenew: Boolean!,
|
billingType: String!, billingAutoRenew: Boolean!,
|
||||||
moderated: Boolean!, nsfw: Boolean!): SubPaidAction!
|
moderated: Boolean!, nsfw: Boolean!): SubPaidAction!
|
||||||
paySub(name: String!): SubPaidAction!
|
paySub(name: String!): SubPaidAction!
|
||||||
@ -24,7 +24,7 @@ export default gql`
|
|||||||
toggleSubSubscription(name: String!): Boolean!
|
toggleSubSubscription(name: String!): Boolean!
|
||||||
transferTerritory(subName: String!, userName: String!): Sub
|
transferTerritory(subName: String!, userName: String!): Sub
|
||||||
unarchiveTerritory(name: String!, desc: String, baseCost: Int!,
|
unarchiveTerritory(name: String!, desc: String, baseCost: Int!,
|
||||||
postTypes: [String!]!,
|
postTypes: [String!]!, allowFreebies: Boolean!,
|
||||||
billingType: String!, billingAutoRenew: Boolean!,
|
billingType: String!, billingAutoRenew: Boolean!,
|
||||||
moderated: Boolean!, nsfw: Boolean!): SubPaidAction!
|
moderated: Boolean!, nsfw: Boolean!): SubPaidAction!
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ import { SubmitButton } from './form'
|
|||||||
|
|
||||||
const FeeButtonContext = createContext()
|
const FeeButtonContext = createContext()
|
||||||
|
|
||||||
export function postCommentBaseLineItems ({ baseCost = 1, comment = false, me }) {
|
export function postCommentBaseLineItems ({ baseCost = 1, comment = false, allowFreebies = true, me }) {
|
||||||
const anonCharge = me
|
const anonCharge = me
|
||||||
? {}
|
? {}
|
||||||
: {
|
: {
|
||||||
@ -29,7 +29,7 @@ export function postCommentBaseLineItems ({ baseCost = 1, comment = false, me })
|
|||||||
term: baseCost,
|
term: baseCost,
|
||||||
label: `${comment ? 'comment' : 'post'} cost`,
|
label: `${comment ? 'comment' : 'post'} cost`,
|
||||||
modifier: (cost) => cost + baseCost,
|
modifier: (cost) => cost + baseCost,
|
||||||
allowFreebies: comment
|
allowFreebies
|
||||||
},
|
},
|
||||||
...anonCharge
|
...anonCharge
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ export function PostForm ({ type, sub, children }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<FeeButtonProvider
|
<FeeButtonProvider
|
||||||
baseLineItems={sub ? postCommentBaseLineItems({ baseCost: sub.baseCost, me: !!me }) : undefined}
|
baseLineItems={sub ? postCommentBaseLineItems({ baseCost: sub.baseCost, allowFreebies: sub.allowFreebies, me: !!me }) : undefined}
|
||||||
useRemoteLineItems={postCommentUseRemoteLineItems({ me: !!me })}
|
useRemoteLineItems={postCommentUseRemoteLineItems({ me: !!me })}
|
||||||
>
|
>
|
||||||
<FormType sub={sub}>{children}</FormType>
|
<FormType sub={sub}>{children}</FormType>
|
||||||
|
@ -91,6 +91,7 @@ export default function TerritoryForm ({ sub }) {
|
|||||||
desc: sub?.desc || '',
|
desc: sub?.desc || '',
|
||||||
baseCost: sub?.baseCost || 10,
|
baseCost: sub?.baseCost || 10,
|
||||||
postTypes: sub?.postTypes || POST_TYPES,
|
postTypes: sub?.postTypes || POST_TYPES,
|
||||||
|
allowFreebies: typeof sub?.allowFreebies === 'undefined' ? true : sub?.allowFreebies,
|
||||||
billingType: sub?.billingType || 'MONTHLY',
|
billingType: sub?.billingType || 'MONTHLY',
|
||||||
billingAutoRenew: sub?.billingAutoRenew || false,
|
billingAutoRenew: sub?.billingAutoRenew || false,
|
||||||
moderated: sub?.moderated || false,
|
moderated: sub?.moderated || false,
|
||||||
@ -132,9 +133,15 @@ export default function TerritoryForm ({ sub }) {
|
|||||||
label='post cost'
|
label='post cost'
|
||||||
name='baseCost'
|
name='baseCost'
|
||||||
type='number'
|
type='number'
|
||||||
|
groupClassName='mb-2'
|
||||||
required
|
required
|
||||||
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
|
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
|
||||||
/>
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label='allow free posts'
|
||||||
|
name='allowFreebies'
|
||||||
|
groupClassName='ms-1'
|
||||||
|
/>
|
||||||
<CheckboxGroup label='post types' name='postTypes'>
|
<CheckboxGroup label='post types' name='postTypes'>
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs={4} sm='auto'>
|
<Col xs={4} sm='auto'>
|
||||||
|
@ -223,10 +223,10 @@ export const UPDATE_COMMENT = gql`
|
|||||||
export const UPSERT_SUB = gql`
|
export const UPSERT_SUB = gql`
|
||||||
${PAID_ACTION}
|
${PAID_ACTION}
|
||||||
mutation upsertSub($oldName: String, $name: String!, $desc: String, $baseCost: Int!,
|
mutation upsertSub($oldName: String, $name: String!, $desc: String, $baseCost: Int!,
|
||||||
$postTypes: [String!]!, $billingType: String!,
|
$postTypes: [String!]!, $allowFreebies: Boolean!, $billingType: String!,
|
||||||
$billingAutoRenew: Boolean!, $moderated: Boolean!, $nsfw: Boolean!) {
|
$billingAutoRenew: Boolean!, $moderated: Boolean!, $nsfw: Boolean!) {
|
||||||
upsertSub(oldName: $oldName, name: $name, desc: $desc, baseCost: $baseCost,
|
upsertSub(oldName: $oldName, name: $name, desc: $desc, baseCost: $baseCost,
|
||||||
postTypes: $postTypes, billingType: $billingType,
|
postTypes: $postTypes, allowFreebies: $allowFreebies, billingType: $billingType,
|
||||||
billingAutoRenew: $billingAutoRenew, moderated: $moderated, nsfw: $nsfw) {
|
billingAutoRenew: $billingAutoRenew, moderated: $moderated, nsfw: $nsfw) {
|
||||||
result {
|
result {
|
||||||
name
|
name
|
||||||
@ -238,10 +238,10 @@ export const UPSERT_SUB = gql`
|
|||||||
export const UNARCHIVE_TERRITORY = gql`
|
export const UNARCHIVE_TERRITORY = gql`
|
||||||
${PAID_ACTION}
|
${PAID_ACTION}
|
||||||
mutation unarchiveTerritory($name: String!, $desc: String, $baseCost: Int!,
|
mutation unarchiveTerritory($name: String!, $desc: String, $baseCost: Int!,
|
||||||
$postTypes: [String!]!, $billingType: String!,
|
$postTypes: [String!]!, $allowFreebies: Boolean!, $billingType: String!,
|
||||||
$billingAutoRenew: Boolean!, $moderated: Boolean!, $nsfw: Boolean!) {
|
$billingAutoRenew: Boolean!, $moderated: Boolean!, $nsfw: Boolean!) {
|
||||||
unarchiveTerritory(name: $name, desc: $desc, baseCost: $baseCost,
|
unarchiveTerritory(name: $name, desc: $desc, baseCost: $baseCost,
|
||||||
postTypes: $postTypes, billingType: $billingType,
|
postTypes: $postTypes, allowFreebies: $allowFreebies, billingType: $billingType,
|
||||||
billingAutoRenew: $billingAutoRenew, moderated: $moderated, nsfw: $nsfw) {
|
billingAutoRenew: $billingAutoRenew, moderated: $moderated, nsfw: $nsfw) {
|
||||||
result {
|
result {
|
||||||
name
|
name
|
||||||
|
@ -7,6 +7,7 @@ export const SUB_FIELDS = gql`
|
|||||||
name
|
name
|
||||||
createdAt
|
createdAt
|
||||||
postTypes
|
postTypes
|
||||||
|
allowFreebies
|
||||||
rankingType
|
rankingType
|
||||||
billingType
|
billingType
|
||||||
billingCost
|
billingCost
|
||||||
|
@ -137,10 +137,6 @@ export const WALLET = gql`
|
|||||||
... on WalletNwc {
|
... on WalletNwc {
|
||||||
nwcUrlRecv
|
nwcUrlRecv
|
||||||
}
|
}
|
||||||
... on WalletPhoenixd {
|
|
||||||
url
|
|
||||||
secondaryPassword
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -177,10 +173,6 @@ export const WALLET_BY_TYPE = gql`
|
|||||||
... on WalletNwc {
|
... on WalletNwc {
|
||||||
nwcUrlRecv
|
nwcUrlRecv
|
||||||
}
|
}
|
||||||
... on WalletPhoenixd {
|
|
||||||
url
|
|
||||||
secondaryPassword
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -762,26 +762,6 @@ export const lncSchema = object({
|
|||||||
.required('required')
|
.required('required')
|
||||||
})
|
})
|
||||||
|
|
||||||
export const phoenixdSchema = object().shape({
|
|
||||||
url: string().url().required('required').trim(),
|
|
||||||
primaryPassword: string().length(64).hex()
|
|
||||||
.when(['secondaryPassword'], ([secondary], schema) => {
|
|
||||||
if (!secondary) return schema.required('required if secondary password not set')
|
|
||||||
return schema.test({
|
|
||||||
test: primary => secondary !== primary,
|
|
||||||
message: 'primary password cannot be the same as secondary password'
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
secondaryPassword: string().length(64).hex()
|
|
||||||
.when(['primaryPassword'], ([primary], schema) => {
|
|
||||||
if (!primary) return schema.required('required if primary password not set')
|
|
||||||
return schema.test({
|
|
||||||
test: secondary => primary !== secondary,
|
|
||||||
message: 'secondary password cannot be the same as primary password'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}, ['primaryPassword', 'secondaryPassword'])
|
|
||||||
|
|
||||||
export const bioSchema = object({
|
export const bioSchema = object({
|
||||||
bio: string().required('required').trim()
|
bio: string().required('required').trim()
|
||||||
})
|
})
|
||||||
|
@ -257,9 +257,9 @@ export default function Settings ({ ssrData }) {
|
|||||||
label={
|
label={
|
||||||
<div className='d-flex align-items-center'>disable freebies
|
<div className='d-flex align-items-center'>disable freebies
|
||||||
<Info>
|
<Info>
|
||||||
<p>Some comments can be created without paying. However, those comments have limited visibility.</p>
|
<p>Some posts and comments can be created without paying. However, that content has limited visibility.</p>
|
||||||
|
|
||||||
<p>If you disable freebies, you will always pay for your comments and get standard visibility.</p>
|
<p>If you disable freebies, you will always pay for your posts and comments and get standard visibility.</p>
|
||||||
|
|
||||||
<p>If you attach a sending wallet, we disable freebies for you unless you have checked/unchecked this value already.</p>
|
<p>If you attach a sending wallet, we disable freebies for you unless you have checked/unchecked this value already.</p>
|
||||||
</Info>
|
</Info>
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
-- AlterEnum
|
|
||||||
ALTER TYPE "WalletType" ADD VALUE 'PHOENIXD';
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "WalletPhoenixd" (
|
|
||||||
"id" SERIAL NOT NULL,
|
|
||||||
"walletId" INTEGER NOT NULL,
|
|
||||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"url" TEXT NOT NULL,
|
|
||||||
"secondaryPassword" TEXT NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "WalletPhoenixd_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "WalletPhoenixd_walletId_key" ON "WalletPhoenixd"("walletId");
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "WalletPhoenixd" ADD CONSTRAINT "WalletPhoenixd_walletId_fkey" FOREIGN KEY ("walletId") REFERENCES "Wallet"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
CREATE TRIGGER wallet_phoenixd_as_jsonb
|
|
||||||
AFTER INSERT OR UPDATE ON "WalletPhoenixd"
|
|
||||||
FOR EACH ROW EXECUTE PROCEDURE wallet_wallet_type_as_jsonb();
|
|
@ -171,7 +171,6 @@ enum WalletType {
|
|||||||
CLN
|
CLN
|
||||||
LNBITS
|
LNBITS
|
||||||
NWC
|
NWC
|
||||||
PHOENIXD
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model Wallet {
|
model Wallet {
|
||||||
@ -197,7 +196,6 @@ model Wallet {
|
|||||||
walletCLN WalletCLN?
|
walletCLN WalletCLN?
|
||||||
walletLNbits WalletLNbits?
|
walletLNbits WalletLNbits?
|
||||||
walletNWC WalletNWC?
|
walletNWC WalletNWC?
|
||||||
walletPhoenixd WalletPhoenixd?
|
|
||||||
withdrawals Withdrawl[]
|
withdrawals Withdrawl[]
|
||||||
InvoiceForward InvoiceForward[]
|
InvoiceForward InvoiceForward[]
|
||||||
|
|
||||||
@ -266,16 +264,6 @@ model WalletNWC {
|
|||||||
nwcUrlRecv String
|
nwcUrlRecv String
|
||||||
}
|
}
|
||||||
|
|
||||||
model WalletPhoenixd {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
walletId Int @unique
|
|
||||||
wallet Wallet @relation(fields: [walletId], references: [id], onDelete: Cascade)
|
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
|
||||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
|
||||||
url String
|
|
||||||
secondaryPassword String
|
|
||||||
}
|
|
||||||
|
|
||||||
model Mute {
|
model Mute {
|
||||||
muterId Int
|
muterId Int
|
||||||
mutedId Int
|
mutedId Int
|
||||||
|
@ -1,184 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
cat <<EOF
|
|
||||||
.__ .__ __
|
|
||||||
____ ____ ______ _ _______ | | | | _____/ |_
|
|
||||||
/ ___\_/ __ \ / \ \/ \/ /\__ \ | | | | _/ __ \ __\\
|
|
||||||
/ /_/ > ___/| | \ / / __ \| |_| |_\ ___/| |
|
|
||||||
\___ / \___ >___| /\/\_/ (____ /____/____/\___ >__|
|
|
||||||
/_____/ \/ \/ \/ \/
|
|
||||||
|
|
||||||
EOF
|
|
||||||
|
|
||||||
error () {
|
|
||||||
echo -n "error: $1"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
wallet=$1
|
|
||||||
[ -z $wallet ] && read -p "Enter wallet name: " wallet
|
|
||||||
[ -z $wallet ] && error "name required"
|
|
||||||
|
|
||||||
# default is wallet in UPPERCASE
|
|
||||||
walletType="${wallet^^}"
|
|
||||||
read -p "Enter walletType (default $walletType): " _walletType
|
|
||||||
if [ ! -z $_walletType ]; then
|
|
||||||
walletType=$_walletType
|
|
||||||
fi
|
|
||||||
|
|
||||||
# default is wallet capitalized with "wallet" prefix
|
|
||||||
walletField="wallet${wallet^}"
|
|
||||||
read -p "Enter walletField (default $walletField): " _walletField
|
|
||||||
if [ ! -z $_walletField ]; then
|
|
||||||
walletField=$_walletField
|
|
||||||
fi
|
|
||||||
|
|
||||||
# exit on first failed command
|
|
||||||
set -e
|
|
||||||
|
|
||||||
todo() {
|
|
||||||
echo "// $wallet::TODO"
|
|
||||||
}
|
|
||||||
|
|
||||||
# create folder and index.js
|
|
||||||
mkdir -p wallets/$wallet
|
|
||||||
cat > wallets/$wallet/index.js <<EOF
|
|
||||||
$(todo)
|
|
||||||
// create validation schema for wallet and import here
|
|
||||||
// import { ${wallet}Schema } from '@/lib/validate'
|
|
||||||
|
|
||||||
export const name = '$wallet'
|
|
||||||
|
|
||||||
$(todo)
|
|
||||||
// configure wallet fields
|
|
||||||
export const fields = []
|
|
||||||
|
|
||||||
$(todo)
|
|
||||||
// configure wallet card
|
|
||||||
export const card = {
|
|
||||||
title: '$wallet',
|
|
||||||
subtitle: '',
|
|
||||||
badges: []
|
|
||||||
}
|
|
||||||
|
|
||||||
$(todo)
|
|
||||||
// set validation schema
|
|
||||||
export const fieldValidation = null // ${wallet}Schema
|
|
||||||
|
|
||||||
export const walletType = '$walletType'
|
|
||||||
|
|
||||||
export const walletField = '$walletField'
|
|
||||||
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# create client.js
|
|
||||||
cat > wallets/$wallet/client.js <<EOF
|
|
||||||
export * from 'wallets/$wallet'
|
|
||||||
|
|
||||||
export async function testSendPayment (config, { logger }) {
|
|
||||||
$(todo)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function sendPayment (bolt11, config) {
|
|
||||||
$(todo)
|
|
||||||
}
|
|
||||||
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# create server.js
|
|
||||||
cat > wallets/$wallet/server.js <<EOF
|
|
||||||
export * from 'wallets/$wallet'
|
|
||||||
|
|
||||||
export async function testCreateInvoice (config) {
|
|
||||||
$(todo)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function createInvoice (
|
|
||||||
{ msats, description, descriptionHash, expiry },
|
|
||||||
config
|
|
||||||
) {
|
|
||||||
$(todo)
|
|
||||||
}
|
|
||||||
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# add TODOs where manual update is needed
|
|
||||||
fragments=fragments/wallet.js
|
|
||||||
i=0
|
|
||||||
grep -n "// XXX \[WALLET\]" $fragments | while read -r match;
|
|
||||||
do
|
|
||||||
lineno=$(echo $match | cut -d':' -f1)
|
|
||||||
sed -i "$((lineno+i))i $(todo)" $fragments
|
|
||||||
i=$((i+1))
|
|
||||||
done
|
|
||||||
|
|
||||||
client=wallets/client.js
|
|
||||||
lineno=$(grep -n "export default" $client | cut -d':' -f1)
|
|
||||||
sed -i "${lineno}i $(todo)" $client
|
|
||||||
|
|
||||||
server=wallets/server.js
|
|
||||||
lineno=$(grep -n "export default" $server | cut -d':' -f1)
|
|
||||||
sed -i "${lineno}i $(todo)" $server
|
|
||||||
|
|
||||||
# need to disable exit on failure since we run grep to check its exit code
|
|
||||||
set +e
|
|
||||||
|
|
||||||
# check if prisma/schema.prisma needs patch
|
|
||||||
schema=prisma/schema.prisma
|
|
||||||
grep --quiet "$walletField" $schema
|
|
||||||
if [ $? -eq 1 ]; then
|
|
||||||
tablename=${walletField^}
|
|
||||||
# find line to insert walletField in wallet model
|
|
||||||
lineno=$(grep -n "model Wallet {" $schema | cut -d':' -f1)
|
|
||||||
offset=$(tail -n +$lineno $schema | grep -nm 1 "}" | cut -d':' -f1)
|
|
||||||
offset=$(tail -n +$lineno $schema | head -n $offset | grep -nE "wallet[[:alpha:]]+\s+ Wallet[[:alpha:]]" | cut -d':' -f1 | tail -n1)
|
|
||||||
sed -i "$((lineno+offset))i\ \ $walletField $tablename?" $schema
|
|
||||||
|
|
||||||
# find line to insert model for wallet
|
|
||||||
lineno=$(grep -nE "model Wallet[[:alpha:]]+ {" $schema | cut -d':' -f1 | tail -n1)
|
|
||||||
offset=$(tail -n +$((lineno+1)) $schema | grep -nm 1 "{" | cut -d':' -f1)
|
|
||||||
i=$((lineno+offset))
|
|
||||||
sed -i "${i}i $(todo)" $schema
|
|
||||||
sed -i "$((i+1))i model Wallet${wallet^} {\n" $schema
|
|
||||||
sed -i "$((i+2))i\ \ id Int @id @default(autoincrement())" $schema
|
|
||||||
sed -i "$((i+3))i\ \ walletId Int @unique" $schema
|
|
||||||
sed -i "$((i+4))i\ \ wallet Wallet @relation(fields: [walletId], references: [id], onDelete: Cascade)" $schema
|
|
||||||
sed -i "$((i+5))i\ \ createdAt DateTime @default(now()) @map(\"created_at\")" $schema
|
|
||||||
sed -i "$((i+6))i\ \ updatedAt DateTime @default(now()) @updatedAt @map(\"updated_at\")" $schema
|
|
||||||
sed -i "$((i+7))i }" $schema
|
|
||||||
|
|
||||||
# find line to insert wallet type
|
|
||||||
lineno=$(grep -nE "enum WalletType {" $schema | cut -d':' -f1)
|
|
||||||
offset=$(tail -n +$lineno $schema | grep -nm 1 "}" | cut -d':' -f1)
|
|
||||||
i=$((lineno+offset-1))
|
|
||||||
sed -i "${i} i\ \ ${walletType}" $schema
|
|
||||||
|
|
||||||
# create migration file with TODOs
|
|
||||||
migrationDir="prisma/migrations/$(date +%Y%m%d%H%M%S_$wallet)"
|
|
||||||
mkdir -p $migrationDir
|
|
||||||
cat > $migrationDir/migration.sql <<EOF
|
|
||||||
-- AlterEnum
|
|
||||||
ALTER TYPE "WalletType" ADD VALUE '${walletType}';
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "$tablename" (
|
|
||||||
"id" SERIAL NOT NULL,
|
|
||||||
"walletId" INTEGER NOT NULL,
|
|
||||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
-- $wallet::TODO
|
|
||||||
|
|
||||||
CONSTRAINT "${tablename}_pkey" PRIMARY KEY ("int")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "${tablename}_walletId_key" ON "$tablename"("walletId");
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "$tablename" ADD CONSTRAINT "${tablename}_walletId_fkey" FOREIGN KEY ("walletId") REFERENCES "Wallet"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
CREATE TRIGGER wallet_${wallet}_as_jsonb
|
|
||||||
AFTER INSERT OR UPDATE ON "$tablename"
|
|
||||||
FOR EACH ROW EXECUTE PROCEDURE wallet_wallet_type_as_jsonb();
|
|
||||||
EOF
|
|
||||||
fi
|
|
@ -6,6 +6,5 @@ import * as cln from 'wallets/cln/client'
|
|||||||
import * as lnd from 'wallets/lnd/client'
|
import * as lnd from 'wallets/lnd/client'
|
||||||
import * as webln from 'wallets/webln/client'
|
import * as webln from 'wallets/webln/client'
|
||||||
import * as blink from 'wallets/blink/client'
|
import * as blink from 'wallets/blink/client'
|
||||||
import * as phoenixd from 'wallets/phoenixd/client'
|
|
||||||
|
|
||||||
export default [nwc, lnbits, lnc, lnAddr, cln, lnd, webln, blink, phoenixd]
|
export default [nwc, lnbits, lnc, lnAddr, cln, lnd, webln, blink]
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
export * from 'wallets/phoenixd'
|
|
||||||
|
|
||||||
export async function testSendPayment (config, { logger }) {
|
|
||||||
// TODO:
|
|
||||||
// Not sure which endpoint to call to test primary password
|
|
||||||
// see https://phoenix.acinq.co/server/api
|
|
||||||
// Maybe just wait until test payments with HODL invoices?
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function sendPayment (bolt11, { url, primaryPassword }) {
|
|
||||||
// https://phoenix.acinq.co/server/api#pay-bolt11-invoice
|
|
||||||
const path = '/payinvoice'
|
|
||||||
|
|
||||||
const headers = new Headers()
|
|
||||||
headers.set('Authorization', 'Basic ' + Buffer.from(':' + primaryPassword).toString('base64'))
|
|
||||||
headers.set('Content-type', 'application/x-www-form-urlencoded')
|
|
||||||
|
|
||||||
const body = new URLSearchParams()
|
|
||||||
body.append('invoice', bolt11)
|
|
||||||
|
|
||||||
const res = await fetch(url + path, {
|
|
||||||
method: 'POST',
|
|
||||||
headers,
|
|
||||||
body
|
|
||||||
})
|
|
||||||
if (!res.ok) {
|
|
||||||
const error = await res.text()
|
|
||||||
throw new Error(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
const payment = await res.json()
|
|
||||||
const preimage = payment.paymentPreimage
|
|
||||||
if (!preimage) {
|
|
||||||
throw new Error(payment.reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
return payment.paymentPreimage
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
import { phoenixdSchema } from '@/lib/validate'
|
|
||||||
|
|
||||||
export const name = 'phoenixd'
|
|
||||||
|
|
||||||
// configure wallet fields
|
|
||||||
export const fields = [
|
|
||||||
{
|
|
||||||
name: 'url',
|
|
||||||
label: 'url',
|
|
||||||
type: 'text'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'primaryPassword',
|
|
||||||
label: 'primary password',
|
|
||||||
type: 'password',
|
|
||||||
optional: 'for sending',
|
|
||||||
help: 'You can find the primary password as `http-password` in your phoenixd configuration file as mentioned [here](https://phoenix.acinq.co/server/api#security).',
|
|
||||||
clientOnly: true,
|
|
||||||
editable: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'secondaryPassword',
|
|
||||||
label: 'secondary password',
|
|
||||||
type: 'password',
|
|
||||||
optional: 'for receiving',
|
|
||||||
help: 'You can find the secondary password as `http-password-limited-access` in your phoenixd configuration file as mentioned [here](https://phoenix.acinq.co/server/api#security).',
|
|
||||||
serverOnly: true,
|
|
||||||
editable: false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
// configure wallet card
|
|
||||||
export const card = {
|
|
||||||
title: 'phoenixd',
|
|
||||||
subtitle: 'use [phoenixd](https://phoenix.acinq.co/server) for payments',
|
|
||||||
badges: ['send & receive']
|
|
||||||
}
|
|
||||||
|
|
||||||
// phoenixd::TODO
|
|
||||||
// set validation schema
|
|
||||||
export const fieldValidation = phoenixdSchema
|
|
||||||
|
|
||||||
export const walletType = 'PHOENIXD'
|
|
||||||
|
|
||||||
export const walletField = 'walletPhoenixd'
|
|
@ -1,38 +0,0 @@
|
|||||||
import { msatsToSats } from '@/lib/format'
|
|
||||||
|
|
||||||
export * from 'wallets/phoenixd'
|
|
||||||
|
|
||||||
export async function testCreateInvoice ({ url, secondaryPassword }) {
|
|
||||||
return await createInvoice(
|
|
||||||
{ msats: 1000, description: 'SN test invoice', expiry: 1 },
|
|
||||||
{ url, secondaryPassword })
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function createInvoice (
|
|
||||||
{ msats, description, descriptionHash, expiry },
|
|
||||||
{ url, secondaryPassword }
|
|
||||||
) {
|
|
||||||
// https://phoenix.acinq.co/server/api#create-bolt11-invoice
|
|
||||||
const path = '/createinvoice'
|
|
||||||
|
|
||||||
const headers = new Headers()
|
|
||||||
headers.set('Authorization', 'Basic ' + Buffer.from(':' + secondaryPassword).toString('base64'))
|
|
||||||
headers.set('Content-type', 'application/x-www-form-urlencoded')
|
|
||||||
|
|
||||||
const body = new URLSearchParams()
|
|
||||||
body.append('description', description)
|
|
||||||
body.append('amountSat', msatsToSats(msats))
|
|
||||||
|
|
||||||
const res = await fetch(url + path, {
|
|
||||||
method: 'POST',
|
|
||||||
headers,
|
|
||||||
body
|
|
||||||
})
|
|
||||||
if (!res.ok) {
|
|
||||||
const error = await res.text()
|
|
||||||
throw new Error(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
const payment = await res.json()
|
|
||||||
return payment.serialized
|
|
||||||
}
|
|
@ -3,13 +3,12 @@ import * as cln from 'wallets/cln/server'
|
|||||||
import * as lnAddr from 'wallets/lightning-address/server'
|
import * as lnAddr from 'wallets/lightning-address/server'
|
||||||
import * as lnbits from 'wallets/lnbits/server'
|
import * as lnbits from 'wallets/lnbits/server'
|
||||||
import * as nwc from 'wallets/nwc/server'
|
import * as nwc from 'wallets/nwc/server'
|
||||||
import * as phoenixd from 'wallets/phoenixd/server'
|
|
||||||
import { addWalletLog } from '@/api/resolvers/wallet'
|
import { addWalletLog } from '@/api/resolvers/wallet'
|
||||||
import walletDefs from 'wallets/server'
|
import walletDefs from 'wallets/server'
|
||||||
import { parsePaymentRequest } from 'ln-service'
|
import { parsePaymentRequest } from 'ln-service'
|
||||||
import { toPositiveNumber } from '@/lib/validate'
|
import { toPositiveNumber } from '@/lib/validate'
|
||||||
import { PAID_ACTION_TERMINAL_STATES } from '@/lib/constants'
|
import { PAID_ACTION_TERMINAL_STATES } from '@/lib/constants'
|
||||||
export default [lnd, cln, lnAddr, lnbits, nwc, phoenixd]
|
export default [lnd, cln, lnAddr, lnbits, nwc]
|
||||||
|
|
||||||
const MAX_PENDING_INVOICES_PER_WALLET = 25
|
const MAX_PENDING_INVOICES_PER_WALLET = 25
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user