Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a3428d1
add hot reloading worker:dev script
huumn Mar 6, 2024
0614cfe
refine docker config
huumn Mar 6, 2024
51dba02
sndev bash script and docker reliability stuff
huumn Mar 7, 2024
fab7503
make posix shell
huumn Mar 7, 2024
f2fd2eb
restart: always -> unless-stopped
huumn Mar 7, 2024
b3bf3db
proper check for postgres health
huumn Mar 7, 2024
af28900
add db seed to sndev
huumn Mar 7, 2024
265f92a
Merge branch 'master' into localdev
huumn Mar 7, 2024
1b27551
refinements after fresh builds
huumn Mar 7, 2024
85c1303
begin adding regtest network
huumn Mar 8, 2024
7fe959a
add changes to .env.sample
huumn Mar 8, 2024
215f330
reorganize docker and add static certs/macroon to lnd
huumn Mar 8, 2024
0e02aa7
copy wallet and macaroon dbs for deterministic wallets/macaroons
huumn Mar 9, 2024
eb7670e
fix perms of shared directories
huumn Mar 9, 2024
bc85e4b
allow debian useradd with duplicate id
huumn Mar 9, 2024
c327825
add auto-mining
huumn Mar 9, 2024
c63622b
make bitcoin health check dependent on blockheight
huumn Mar 9, 2024
c88ba74
open channel between ln nodes
huumn Mar 10, 2024
e824261
improve channel opens
huumn Mar 10, 2024
714aecc
add sndev payinvoice
huumn Mar 10, 2024
0500cbd
add sndev withdraw
huumn Mar 10, 2024
4ce413a
ascii art
huumn Mar 10, 2024
8cddbe7
add sndev status
huumn Mar 10, 2024
a31d04a
sndev passthrough to docker and containers
huumn Mar 10, 2024
5572791
add sndev psql command
huumn Mar 10, 2024
4b2596f
remove script logging
huumn Mar 10, 2024
09a30a4
small script cleanup
huumn Mar 10, 2024
fa3aed2
smaller db seed
huumn Mar 11, 2024
fc5a4c5
pin opensearch version
huumn Mar 11, 2024
b374758
pin opensearch dashboard
huumn Mar 11, 2024
1e9d40f
add sndev prisma
huumn Mar 11, 2024
32bc3f1
add help for all commands
huumn Mar 11, 2024
bb41692
set -e
huumn Mar 11, 2024
72ecc7b
s3 and image proxy with broken name resolution
huumn Mar 12, 2024
8a96fbb
finally fully working image uploads
huumn Mar 12, 2024
46effa6
use a better diff algo
huumn Mar 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 87 additions & 42 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@
############################################################################

# github
GITHUB_ID=<YOUR GITHUB ID>
GITHUB_SECRET=<YOUR GITHUB SECRET>
GITHUB_ID=
GITHUB_SECRET=

# twitter
TWITTER_ID=<YOUR TWITTER ID>
TWITTER_SECRET=<YOUR TWITTER SECRET>
TWITTER_ID=
TWITTER_SECRET=

# email
LOGIN_EMAIL_SERVER=smtp://<YOUR EMAIL>:<YOUR PASSWORD>@<YOUR SMTP DOMAIN>:587
LOGIN_EMAIL_FROM=<YOUR FROM ALIAS>
LOGIN_EMAIL_SERVER=
LOGIN_EMAIL_FROM=
LIST_MONK_AUTH=

#####################################################################
# OTHER / OPTIONAL #
# configuration for push notifications, slack and imgproxy are here #
#####################################################################
########################################################
# OTHER / OPTIONAL #
# configuration for push notifications, slack are here #
########################################################

# VAPID for Web Push
VAPID_MAILTO=
Expand All @@ -30,38 +30,13 @@ VAPID_PRIVKEY=
SLACK_BOT_TOKEN=
SLACK_CHANNEL_ID=

# imgproxy
NEXT_PUBLIC_IMGPROXY_URL=
IMGPROXY_KEY=
IMGPROXY_SALT=

# search
OPENSEARCH_URL=http://opensearch:9200
OPENSEARCH_USERNAME=
OPENSEARCH_PASSWORD=
OPENSEARCH_INDEX=item
OPENSEARCH_MODEL_ID=
# lnurl ... you'll need a tunnel to localhost:3000 for these
LNAUTH_URL=
LNWITH_URL=

#######################################################
# WALLET / OPTIONAL #
# if you want to work with payments you'll need these #
#######################################################

# lnd
LND_CERT=<YOUR LND HEX CERT>
LND_MACAROON=<YOUR LND HEX MACAROON>
LND_SOCKET=<YOUR LND GRPC HOST>:<YOUR LND GRPC PORT>

# lnurl
LNAUTH_URL=<PUBLIC URL TO /api/lnauth>
LNWITH_URL=<PUBLIC URL TO /api/lnwith>

# nostr (NIP-57 zap receipts)
NOSTR_PRIVATE_KEY=<YOUR NOSTR PRIVATE KEY IN HEX>

###############
# LEAVE AS IS #
###############
#########################
# SNDEV STUFF WE PRESET #
#########################

# static things
NEXTAUTH_URL=http://localhost:3000/api/auth
Expand All @@ -72,7 +47,21 @@ NEXTAUTH_SECRET=3_0W_PhDRZVanbeJsZZGIEljexkKoGbL6qGIqSwTjjI
JWT_SIGNING_PRIVATE_KEY={"kty":"oct","kid":"FvD__hmeKoKHu2fKjUrWbRKfhjimIM4IKshyrJG4KSM","alg":"HS512","k":"3_0W_PhDRZVanbeJsZZGIEljexkKoGbL6qGIqSwTjjI"}
INVOICE_HMAC_KEY=a4c1d9c81edb87b79d28809876a18cf72293eadb39f92f3f4f2f1cfbdf907c91

# imgproxy options
# lnd
# xxd -p -c0 docker/lnd/sn/regtest/admin.macaroon
LND_CERT=2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d494943516a43434165696741774942416749516139493834682b48653350385a437541525854554d54414b42676771686b6a4f50515144416a41344d5238770a485159445651514b45785a73626d5167595856306232646c626d56795958526c5a43426a5a584a304d5255774577594456515144457778694e6a41785a5749780a4d474d354f444d774868634e4d6a51774d7a41334d5463774d6a45355768634e4d6a55774e5441794d5463774d6a4535576a41344d523877485159445651514b0a45785a73626d5167595856306232646c626d56795958526c5a43426a5a584a304d5255774577594456515144457778694e6a41785a5749784d474d354f444d770a5754415442676371686b6a4f5051494242676771686b6a4f50514d4242774e4341415365596a4b62542b4a4a4a37624b6770677a6d6c3278496130364e3174680a2f4f7033533173382b4f4a41387836647849682f326548556b4f7578675a36703549434b496f375a544c356a5963764375793941334b6e466f3448544d4948510a4d41344741315564447745422f775145417749437044415442674e56485355454444414b4267677242674546425163444154415042674e5648524d42416638450a425441444151482f4d4230474131556444675157424252545756796e653752786f747568717354727969466d6a36736c557a423542674e5648524545636a42770a676778694e6a41785a5749784d474d354f444f4343577876593246736147397a64494947633235666247356b6768526f62334e304c6d52765932746c636935700a626e526c636d356862494945645735706549494b64573570654842685932746c64494948596e566d59323975626f6345667741414159635141414141414141410a41414141414141414141414141596345724273414254414b42676771686b6a4f5051514441674e4941444246416945413873616c4a667134476671465557532f0a35347a335461746c6447736673796a4a383035425a5263334f326f434943794e6e3975716976566f5575365935345143624c3966394c575779547a516e61616e0a656977482f51696b0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a
LND_MACAROON=0201036c6e6402f801030a106cf4e146abffa5d766befbbf4c73b5a31201301a160a0761646472657373120472656164120577726974651a130a04696e666f120472656164120577726974651a170a08696e766f69636573120472656164120577726974651a210a086d616361726f6f6e120867656e6572617465120472656164120577726974651a160a076d657373616765120472656164120577726974651a170a086f6666636861696e120472656164120577726974651a160a076f6e636861696e120472656164120577726974651a140a057065657273120472656164120577726974651a180a067369676e6572120867656e6572617465120472656164000006202c3bfd55c191e925cbffd73712c9d4b9b4a8440410bde5f8a0a6e33af8b3d876
LND_SOCKET=sn_lnd:10009

# nostr (NIP-57 zap receipts)
# openssl rand -hex 32
NOSTR_PRIVATE_KEY=5f30b7e7714360f51f2be2e30c1d93b7fdf67366e730658e85777dfcc4e4245f

# imgproxy
NEXT_PUBLIC_IMGPROXY_URL=http://localhost:3001
IMGPROXY_KEY=9c273e803fd5d444bf8883f8c3000de57bee7995222370cab7f2d218dd9a4bbff6ca11cbf902e61eeef4358616f231da51e183aee6841e3a797a5c9a9530ba67
IMGPROXY_SALT=47b802be2c9250a66b998f411fc63912ab0bc1c6b47d99b8d37c61019d1312a984b98745eac83db9791b01bb8c93ecbc9b2ef9f2981d66061c7d0a4528ff6465

IMGPROXY_ENABLE_WEBP_DETECTION=1
IMGPROXY_ENABLE_AVIF_DETECTION=1
IMGPROXY_MAX_ANIMATION_FRAMES=2000
Expand All @@ -84,11 +73,67 @@ IMGPROXY_DOWNLOAD_TIMEOUT=9
# IMGPROXY_DEVELOPMENT_ERRORS_MODE=1
# IMGPROXY_ENABLE_DEBUG_HEADERS=true

NEXT_PUBLIC_AWS_UPLOAD_BUCKET=uploads
NEXT_PUBLIC_MEDIA_DOMAIN=localhost:4566
NEXT_PUBLIC_MEDIA_URL=http://localhost:4566/uploads

# search
OPENSEARCH_URL=http://opensearch:9200
OPENSEARCH_USERNAME=
OPENSEARCH_PASSWORD=
OPENSEARCH_INDEX=item
OPENSEARCH_MODEL_ID=

# prisma db url
DATABASE_URL="postgresql://sn:password@db:5432/stackernews?schema=public"

###################
# FOR DOCKER ONLY #
###################

# containers can't use localhost, so we need to use the container name
IMGPROXY_URL_DOCKER=http://imgproxy:8080
MEDIA_URL_DOCKER=http://s3:4566/uploads

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spent the better part of 10 hours trying to avoid this 😮‍💨 and all the related changes.

Things that didn't work:

  • any kind of normal docker compose configuration that uses the default network
  • running a reverse proxy container
    • I almost got it working with imgproxy.localhost and s3.localhost and docker compose links but after I got that all figured out, I learned macos doesn't support localhost subdomains
  • intercepting fetches in the service worker transforming domains, e.g. imgproxy:3001 -> localhost:3001

... I'm not sure what else I could have tried. We more or less need the host, app, worker, s3, and imgproxy to all share some kind of name resolution service. This is easy enough to configure in containers, but we can't reliably alter (nor should we) the host's dns.

# postgres container stuff
POSTGRES_PASSWORD=password
POSTGRES_USER=sn
POSTGRES_DB=stackernews

# opensearch container stuff
OPENSEARCH_INITIAL_ADMIN_PASSWORD=mVchg1T5oA9wudUh
plugins.security.disabled=true
discovery.type=single-node
DISABLE_SECURITY_DASHBOARDS_PLUGIN=true

# bitcoind container stuff
RPC_AUTH='7c68e5fcdba94a366bfdf629ecc676bb$0d0fc087c3bf7f068f350292bf8de1418df3dd8cb31e35682d5d3108d601002b'
RPC_USER=bitcoin
RPC_PASS=bitcoin
RPC_PORT=18443
P2P_PORT=18444
ZMQ_BLOCK_PORT=28334
ZMQ_TX_PORT=28335

# sn lnd container stuff
LND_REST_PORT=8080
LND_GRPC_PORT=10009
LND_P2P_PORT=9735
# docker exec -u lnd sn_lnd lncli newaddress p2wkh --unused
LND_ADDR=bcrt1q7q06n5st4vqq3lssn0rtkrn2qqypghv9xg2xnl
LND_PUBKEY=02cb2e2d5a6c5b17fa67b1a883e2973c82e328fb9bd08b2b156a9e23820c87a490

# stacker lnd container stuff
STACKER_LND_REST_PORT=8081
STACKER_LND_GRPC_PORT=10010
# docker exec -u lnd stacker_lnd lncli newaddress p2wkh --unused
STACKER_LND_ADDR=bcrt1qfqau4ug9e6rtrvxrgclg58e0r93wshucumm9vu
STACKER_LND_PUBKEY=028093ae52e011d45b3e67f2e0f2cb6c3a1d7f88d2920d408f3ac6db3a56dc4b35

LNCLI_NETWORK=regtest

# localstack container stuff
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
PERSISTENCE=1
SKIP_SSL_CERT_DOWNLOAD=1
7 changes: 2 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,9 @@ yarn-debug.log*
yarn-error.log*

# local env files
.env
envbak
.env.local
.env.development.local
.env.test.local
.env.production.local
.env*
!.env.sample

# vercel
.vercel
Expand Down
10 changes: 9 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,16 @@ FROM node:18.17.0-bullseye

ENV NODE_ENV=development

ARG UID
ARG GID
RUN groupadd -fg "$GID" apprunner
RUN useradd -om -u "$UID" -g "$GID" apprunner
USER apprunner

WORKDIR /app

EXPOSE 3000

CMD npm install --loglevel verbose --legacy-peer-deps; npx prisma migrate dev; npm run dev
COPY package.json package-lock.json ./
RUN npm ci --legacy-peer-deps --loglevel verbose
CMD ["sh","-c","npm install --loglevel verbose --legacy-peer-deps && npx prisma migrate dev && npm run dev"]
15 changes: 13 additions & 2 deletions api/s3/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import AWS from 'aws-sdk'
import { MEDIA_URL } from '../../lib/constants'

const bucketRegion = 'us-east-1'
const Bucket = process.env.NEXT_PUBLIC_AWS_UPLOAD_BUCKET
Expand All @@ -7,8 +8,18 @@ AWS.config.update({
region: bucketRegion
})

const config = {
apiVersion: '2006-03-01',
endpoint: process.env.NODE_ENV === 'development' ? `${MEDIA_URL}` : undefined,
s3ForcePathStyle: process.env.NODE_ENV === 'development'
}

export function createPresignedPost ({ key, type, size }) {
const s3 = new AWS.S3({ apiVersion: '2006-03-01' })
const s3 = new AWS.S3({
...config,
// in development, we need to be able to call this from localhost
endpoint: process.env.NODE_ENV === 'development' ? `${process.env.NEXT_PUBLIC_MEDIA_URL}` : undefined
})
return new Promise((resolve, reject) => {
s3.createPresignedPost({
Bucket,
Expand All @@ -25,7 +36,7 @@ export function createPresignedPost ({ key, type, size }) {
}

export async function deleteObjects (keys) {
const s3 = new AWS.S3({ apiVersion: '2006-03-01' })
const s3 = new AWS.S3(config)
// max 1000 keys per request
// see https://docs.aws.amazon.com/cli/latest/reference/s3api/delete-objects.html
const batchSize = 1000
Expand Down
11 changes: 9 additions & 2 deletions components/image.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { IMGPROXY_URL_REGEXP } from '../lib/url'
import { useShowModal } from './modal'
import { useMe } from './me'
import { Dropdown } from 'react-bootstrap'
import { UNKNOWN_LINK_REL, UPLOAD_TYPES_ALLOW } from '../lib/constants'
import { UNKNOWN_LINK_REL, UPLOAD_TYPES_ALLOW, MEDIA_URL } from '../lib/constants'
import { useToast } from './toast'
import gql from 'graphql-tag'
import { useMutation } from '@apollo/client'
Expand Down Expand Up @@ -68,6 +68,10 @@ function ImageProxy ({ src, srcSet: { dimensions, ...srcSetObj } = {}, onClick,
if (!srcSetObj) return undefined
// srcSetObj shape: { [widthDescriptor]: <imgproxyUrl>, ... }
return Object.entries(srcSetObj).reduce((acc, [wDescriptor, url], i, arr) => {
// backwards compatibility: we used to replace image urls with imgproxy urls rather just storing paths
if (!url.startsWith('http')) {
url = `${process.env.NEXT_PUBLIC_IMGPROXY_URL}${url}`
}
return acc + `${url} ${wDescriptor}` + (i < arr.length - 1 ? ', ' : '')
}, '')
}, [srcSetObj])
Expand All @@ -77,6 +81,9 @@ function ImageProxy ({ src, srcSet: { dimensions, ...srcSetObj } = {}, onClick,
const bestResSrc = useMemo(() => {
if (!srcSetObj) return src
return Object.entries(srcSetObj).reduce((acc, [wDescriptor, url]) => {
if (!url.startsWith('http')) {
url = `${process.env.NEXT_PUBLIC_IMGPROXY_URL}${url}`
}
const w = Number(wDescriptor.replace(/w$/, ''))
return w > acc.w ? { w, url } : acc
}, { w: 0, url: undefined }).url
Expand Down Expand Up @@ -224,7 +231,7 @@ export const ImageUpload = forwardRef(({ children, className, onSelect, onUpload
return
}

const url = `https://${process.env.NEXT_PUBLIC_MEDIA_DOMAIN}/${data.getSignedPOST.fields.key}`
const url = `${MEDIA_URL}/${data.getSignedPOST.fields.key}`
// key is upload id in database
const id = data.getSignedPOST.fields.key
onSuccess?.({ ...variables, id, name: file.name, url, file })
Expand Down
3 changes: 2 additions & 1 deletion components/item-job.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { timeSince } from '../lib/time'
import EmailIcon from '../svgs/mail-open-line.svg'
import Share from './share'
import Hat from './hat'
import { MEDIA_URL } from '../lib/constants'

export default function ItemJob ({ item, toc, rank, children }) {
const isEmail = string().email().isValidSync(item.url)
Expand All @@ -25,7 +26,7 @@ export default function ItemJob ({ item, toc, rank, children }) {
<div className={styles.item}>
<Link href={`/items/${item.id}`}>
<Image
src={item.uploadId ? `https://${process.env.NEXT_PUBLIC_MEDIA_DOMAIN}/${item.uploadId}` : '/jobs-default.png'} width='42' height='42' className={styles.companyImage}
src={item.uploadId ? `${MEDIA_URL}/${item.uploadId}` : '/jobs-default.png'} width='42' height='42' className={styles.companyImage}
/>
</Link>
<div className={`${styles.hunk} align-self-center mb-0`}>
Expand Down
4 changes: 2 additions & 2 deletions components/job-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import Link from 'next/link'
import { usePrice } from './price'
import Avatar from './avatar'
import { jobSchema } from '../lib/validate'
import { MAX_TITLE_LENGTH } from '../lib/constants'
import { MAX_TITLE_LENGTH, MEDIA_URL } from '../lib/constants'
import { useToast } from './toast'
import { toastDeleteScheduled } from '../lib/form'
import { ItemButtonBar } from './post'
Expand Down Expand Up @@ -110,7 +110,7 @@ export default function JobForm ({ item, sub }) {
<label className='form-label'>logo</label>
<div className='position-relative' style={{ width: 'fit-content' }}>
<Image
src={logoId ? `https://${process.env.NEXT_PUBLIC_MEDIA_DOMAIN}/${logoId}` : '/jobs-default.png'} width='135' height='135' roundedCircle
src={logoId ? `${MEDIA_URL}/${logoId}` : '/jobs-default.png'} width='135' height='135' roundedCircle
/>
<Avatar onSuccess={setLogoId} />
</div>
Expand Down
4 changes: 2 additions & 2 deletions components/user-header.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { hexToBech32 } from '../lib/nostr'
import NostrIcon from '../svgs/nostr.svg'
import GithubIcon from '../svgs/github-fill.svg'
import TwitterIcon from '../svgs/twitter-fill.svg'
import { UNKNOWN_LINK_REL } from '../lib/constants'
import { UNKNOWN_LINK_REL, MEDIA_URL } from '../lib/constants'

export default function UserHeader ({ user }) {
const router = useRouter()
Expand Down Expand Up @@ -96,7 +96,7 @@ function HeaderPhoto ({ user, isMe }) {
}
}
)
const src = user.photoId ? `https://${process.env.NEXT_PUBLIC_MEDIA_DOMAIN}/${user.photoId}` : '/dorian400.jpg'
const src = user.photoId ? `${MEDIA_URL}/${user.photoId}` : '/dorian400.jpg'

return (
<div className='position-relative align-self-start' style={{ width: 'fit-content' }}>
Expand Down
3 changes: 2 additions & 1 deletion components/user-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useQuery } from '@apollo/client'
import MoreFooter from './more-footer'
import { useData } from './use-data'
import Hat from './hat'
import { MEDIA_URL } from '../lib/constants'

// all of this nonsense is to show the stat we are sorting by first
const Stacked = ({ user }) => (user.optional.stacked !== null && <span>{abbrNum(user.optional.stacked)} stacked</span>)
Expand Down Expand Up @@ -48,7 +49,7 @@ function User ({ user, rank, statComps, Embellish }) {
<div className={`${styles.item} mb-2`}>
<Link href={`/${user.name}`}>
<Image
src={user.photoId ? `https://${process.env.NEXT_PUBLIC_MEDIA_DOMAIN}/${user.photoId}` : '/dorian400.jpg'} width='32' height='32'
src={user.photoId ? `${MEDIA_URL}/${user.photoId}` : '/dorian400.jpg'} width='32' height='32'
className={`${userStyles.userimg} me-2`}
/>
</Link>
Expand Down
Loading