Skip to content

Commit fa3b9a2

Browse files
authored
Merge branch 'master' into custom_domain
2 parents 2acbb44 + 5b7ff24 commit fa3b9a2

20 files changed

+269
-76
lines changed

.github/workflows/extend-awards.yml

+17-10
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,35 @@
11
name: extend-awards
2-
run-name: Extending awards
32
on:
4-
pull_request:
3+
pull_request_target:
54
types: [ closed ]
65
branches:
76
- master
7+
permissions:
8+
pull-requests: write
9+
contents: write
10+
issues: read
811
jobs:
9-
unfiltered:
10-
runs-on: ubuntu-latest
11-
steps:
12-
- run: echo '${{ toJson(github) }}'
1312
if_merged:
14-
if: github.event_name == 'pull_request' && github.event.action == 'closed' && github.event.pull_request.merged == true
13+
if: |
14+
github.event_name == 'pull_request_target' &&
15+
github.event.action == 'closed' &&
16+
github.event.pull_request.merged == true &&
17+
github.event.pull_request.head.ref != 'extend-awards/patch'
1518
runs-on: ubuntu-latest
1619
steps:
1720
- uses: actions/checkout@v4
1821
- uses: actions/setup-python@v5
1922
with:
2023
python-version: '3.13'
2124
- run: pip install requests
22-
- run: python extend-awards.py '${{ toJson(github) }}'
25+
- run: python extend-awards.py
2326
env:
2427
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28+
GITHUB_CONTEXT: ${{ toJson(github) }}
2529
- uses: peter-evans/create-pull-request@v7
2630
with:
27-
commit-message: extending awards
28-
title: Extending awards
31+
add-paths: awards.csv
32+
branch: extend-awards/patch
33+
commit-message: Extending awards.csv
34+
title: Extending awards.csv
35+
body: A PR was merged that solves an issue and awards.csv should be extended.

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,5 @@ docker/lnbits/data
6464
!docker/lndk/tls-*.pem
6565

6666
# nostr link extract
67-
scripts/nostr-link-extract.config.json
67+
scripts/nostr-link-extract.config.json
68+
scripts/nostr-links.db

api/paidAction/receive.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ export const anonable = false
77

88
export const paymentMethods = [
99
PAID_ACTION_PAYMENT_METHODS.P2P,
10-
PAID_ACTION_PAYMENT_METHODS.DIRECT,
11-
PAID_ACTION_PAYMENT_METHODS.OPTIMISTIC
10+
PAID_ACTION_PAYMENT_METHODS.DIRECT
1211
]
1312

1413
export async function getCost ({ msats }) {

api/resolvers/admin.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { SN_ADMIN_IDS } from '@/lib/constants'
2+
13
export default {
24
Query: {
35
snl: async (parent, _, { models }) => {
@@ -7,7 +9,7 @@ export default {
79
},
810
Mutation: {
911
onAirToggle: async (parent, _, { models, me }) => {
10-
if (me.id !== 616) {
12+
if (!me || !SN_ADMIN_IDS.includes(me.id)) {
1113
throw new Error('not an admin')
1214
}
1315
const { id, live } = await models.snl.findFirst()

awards.csv

+13-2
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,17 @@ SatsAllDay,issue,#1820,#1819,easy,,,1,9k,[email protected],2025-01-27
175175
Soxasora,pr,#1814,#1736,easy,,,,100k,[email protected],2025-01-27
176176
jason-me,pr,#1857,,easy,,,,100k,[email protected],2025-02-08
177177
ed-kung,pr,#1901,#323,good-first-issue,,,,20k,[email protected],2025-02-14
178-
Scroogey-SN,pr,#1911,#1905,good-first-issue,,,1,18k,???,???
179-
Scroogey-SN,pr,#1928,#1924,good-first-issue,,,,20k,???,???
178+
Scroogey-SN,pr,#1911,#1905,good-first-issue,,,1,18k,[email protected],???
179+
Scroogey-SN,pr,#1928,#1924,good-first-issue,,,,20k,[email protected],???
180180
dtonon,issue,#1928,#1924,good-first-issue,,,,2k,???,???
181+
ed-kung,pr,#1926,#1914,medium-hard,,,,500k,[email protected],???
182+
ed-kung,issue,#1926,#1914,medium-hard,,,,50k,[email protected],???
183+
ed-kung,pr,#1926,#1927,easy,,,,100k,[email protected],???
184+
ed-kung,issue,#1926,#1927,easy,,,,10k,[email protected],???
185+
ed-kung,issue,#1913,#1890,good-first-issue,,,,2k,[email protected],???
186+
Scroogey-SN,pr,#1930,#1167,good-first-issue,,,,20k,[email protected],???
187+
itsrealfake,issue,#1930,#1167,good-first-issue,,,,2k,[email protected],???
188+
Scroogey-SN,pr,#1948,#1849,medium,urgent,,,750k,[email protected],???
189+
felipebueno,issue,#1947,#1945,good-first-issue,,,,2k,[email protected],???
190+
ed-kung,pr,#1952,#1951,easy,,,,100k,[email protected],???
191+
ed-kung,issue,#1952,#1951,easy,,,,10k,[email protected],???

components/link-to-context.module.css

+2-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
.linkBoxParent input,
3232
.linkBoxParent iframe,
3333
.linkBoxParent video,
34+
.linkBoxParent pre,
3435
.linkBoxParent img {
3536
pointer-events: auto !important;
36-
}
37+
}

components/notifications.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,10 @@ export function NotificationAlert () {
733733
error
734734
? (
735735
<Alert variant='danger' dismissible onClose={() => setError(null)}>
736-
<span>{error.toString()}</span>
736+
<span>{navigator?.brave && error.name === 'AbortError'
737+
? 'Push registration failed. Enable "Use Google services for push messaging" in Brave\'s privacy settings and try again.'
738+
: error.toString()}
739+
</span>
737740
</Alert>
738741
)
739742
: showAlert

components/text.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,13 @@ export function SearchText ({ text }) {
4949

5050
// this is one of the slowest components to render
5151
export default memo(function Text ({ rel = UNKNOWN_LINK_REL, imgproxyUrls, children, tab, itemId, outlawed, topLevel }) {
52+
// would the text overflow on the current screen size?
5253
const [overflowing, setOverflowing] = useState(false)
53-
const router = useRouter()
54+
// should we show the full text?
5455
const [show, setShow] = useState(false)
5556
const containerRef = useRef(null)
57+
58+
const router = useRouter()
5659
const [mathJaxPlugin, setMathJaxPlugin] = useState(null)
5760

5861
// we only need mathjax if there's math content between $$ tags
@@ -69,9 +72,9 @@ export default memo(function Text ({ rel = UNKNOWN_LINK_REL, imgproxyUrls, child
6972

7073
// if we are navigating to a hash, show the full text
7174
useEffect(() => {
72-
setShow(router.asPath.includes('#') && !router.asPath.includes('#itemfn-'))
75+
setShow(router.asPath.includes('#'))
7376
const handleRouteChange = (url, { shallow }) => {
74-
setShow(url.includes('#') && !url.includes('#itemfn-'))
77+
setShow(url.includes('#'))
7578
}
7679

7780
router.events.on('hashChangeStart', handleRouteChange)

components/text.module.css

+5-4
Original file line numberDiff line numberDiff line change
@@ -195,15 +195,16 @@
195195
.p:has(> .mediaContainer) .mediaContainer img,
196196
.p:has(> .mediaContainer) .mediaContainer video
197197
{
198-
block-size: revert-layer;
198+
min-width: 30%;
199199
max-width: stretch;
200200
}
201201

202-
.p.onlyImages {
203-
display: block;
202+
.p:has(> .mediaContainer) .mediaContainer img
203+
{
204+
block-size: revert-layer;
204205
}
205206

206-
.p.onlyImages:has(> .mediaContainer.loaded) {
207+
.p.onlyImages {
207208
display: flex;
208209
flex-direction: row;
209210
flex-wrap: wrap;

docs/dev/extend-awards.md

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Automatically extend awards.csv
2+
3+
## Overview
4+
5+
Whenever a pull request (PR) is merged in the [stacker.news](https://github.com/stackernews/stacker.news) repository, a [GitHub Action](https://docs.github.com/en/actions) is triggered:
6+
7+
If the merged PR solves an issue with [award tags](https://github.com/stackernews/stacker.news?tab=readme-ov-file#contributing),
8+
the amounts due to the PR and issue authors are calculated and corresponding lines are added to the [awards.csv](https://github.com/stackernews/stacker.news/blob/master/awards.csv) file,
9+
and a PR is opened for this change.
10+
11+
## Action
12+
13+
The action is defined in [.github/workflows/extend-awards.yml](.github/workflows/extend-awards.yml).
14+
15+
Filters on the event type and parameters ensure the action is [triggered only on merged PRs](https://stackoverflow.com/questions/60710209/trigger-github-actions-only-when-pr-is-merged).
16+
17+
The primary job consists of several steps:
18+
- [checkout](https://github.com/actions/checkout) checks out the repository
19+
- [setup-python](https://github.com/actions/setup-python) installs [Python](https://en.wikipedia.org/wiki/Python_(programming_language))
20+
- [pip](https://en.wikipedia.org/wiki/Pip_%28package_manager%29) installs the [requests](https://docs.python-requests.org/en/latest/index.html) module
21+
- a script (see below) is executed, which appends lines to [awards.csv](awards.csv) if needed
22+
- [create-pull-request](https://github.com/peter-evans/create-pull-request) looks for modified files and creates (or updates) a PR
23+
24+
## Script
25+
26+
The script is [extend-awards.py](extend-awards.py).
27+
28+
The script extracts from the [environment](https://en.wikipedia.org/wiki/Environment_variable) an authentication token needed for the [GitHub REST API](https://docs.github.com/en/rest/about-the-rest-api/about-the-rest-api) and the [context](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs) containing the event details including the merged PR (formatted in [JSON](https://en.wikipedia.org/wiki/JSON)).
29+
30+
In the merged PR's title and body it searches for the first [GitHub issue URL](https://github.com/stackernews/stacker.news/issues/) or any number with a hash symbol (#) prefix, and takes this as the issue being solved by the PR.
31+
32+
Using the GitHub REST API it fetches the issue and analyzes its tags for difficulty and priority.
33+
34+
It fetches the issue's timeline and counts the number of reviews completed with status 'changes requested' to calculate the amount reduction.
35+
36+
It calculates the amounts due to the PR author and the issue author.
37+
38+
It reads the existing awards.csv file to suppress appending redundant lines (same user, PR, and issue) and fill known receive methods (same user).
39+
40+
Finally, it appends zero, one, or two lines to the awards.csv file.
41+
42+
## Diagnostics
43+
44+
In the GitHub web interface under 'Actions' each invokation of the action can be viewed, including environment and [output and errors](https://en.wikipedia.org/wiki/Standard_streams) of the script. First, the specific invokation is selected, then the job 'if_merged', then the step 'Run python extend-awards.py'. The environment is found by expanding the inner 'Run python extended-awards.py' on the first line.
45+
46+
The normal output includes details about the issue number found, the amount calculation, or the reason for not appending lines.
47+
48+
The error output may include a [Python traceback](https://realpython.com/python-traceback/) which helps to explain the error.
49+
50+
The environment contains in GITHUB_CONTEXT the event details, which may be required to understand the error.
51+
52+
## Security considerations
53+
54+
The create-pull-request step requires [workflow permissions](https://github.com/peter-evans/create-pull-request#workflow-permissions).

docs/dev/semantic-search.md

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Getting semantic search setup in OpenSearch is a multistep process.
1+
Getting semantic search setup in OpenSearch is currently a multistep, manual process. To configure semantic search, enter the following commands into OpenSearch's REST API. You can do this in Dev Tools in the OpenSearch Dashboard (after starting your SN dev environment, point your browser to localhost:5601). You can also use CURL to send these commands to localhost:9200.
22

33
### step 1: configure the ml plugin
44
```json
@@ -67,7 +67,7 @@ PUT /_ingest/pipeline/nlp-ingest-pipeline
6767
},
6868
{
6969
"text_embedding": {
70-
"model_id": "6whlBY0B2sj1ObjeeD5d",
70+
"model_id": "<model id>",
7171
"field_map": {
7272
"text": "text_embedding",
7373
"title": "title_embedding"
@@ -306,3 +306,13 @@ GET /item-nlp/_search
306306
}
307307
```
308308

309+
### step 12: configure the development environment to use the nlp pipeline
310+
311+
Add the following lines to `.env.local`:
312+
313+
```
314+
OPENSEARCH_INDEX=item-nlp
315+
OPENSEARCH_MODEL_ID=<model id>
316+
```
317+
318+
Note that you won't have to re-do the above steps each time you restart your dev instance. The OpenSearch configuration is saved to a local volume.

extend-awards.py

+5-17
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import json, os, re, requests, sys
1+
import json, os, re, requests
22

33
difficulties = {'good-first-issue':20000,'easy':100000,'medium':250000,'medium-hard':500000,'hard':1000000}
44
priorities = {'low':0.5,'medium':1.5,'high':2,'urgent':3}
@@ -56,11 +56,8 @@ def countReviews(pr):
5656
count += 1
5757
return count
5858

59-
def checkPR(pr):
60-
i = getIssue(pr)
61-
if not 'pull_request' in i or not 'merged_at' in i['pull_request']:
62-
print('pr %s is not a merged pull request' % pr)
63-
return
59+
def checkPR(i):
60+
pr = str(i['number'])
6461
print('pr %s' % pr)
6562
n = findIssueInPR(i)
6663
if not n:
@@ -103,14 +100,5 @@ def checkPR(pr):
103100
s = s.split('\n')[0]
104101
awards.append(s.split(','))
105102

106-
j = json.loads(sys.argv[1])
107-
url = j['event']['pull_request']['_links']['commits']['href']
108-
r = sess.get(url, headers=headers)
109-
j = json.loads(r.text)
110-
for c in j:
111-
m = re.search('\\(#([0-9]+)\\)$', c['commit']['message'].split('\n')[0])
112-
if m:
113-
checkPR(m.group(1))
114-
exit(0)
115-
print('no PR found in commit')
116-
103+
j = json.loads(os.getenv('GITHUB_CONTEXT'))
104+
checkPR(j['event']['pull_request'])

lib/constants.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,7 @@ export const RESERVED_MAX_USER_ID = 615
9292
export const GLOBAL_SEED = USER_ID.k00b
9393
export const FREEBIE_BASE_COST_THRESHOLD = 10
9494

95-
// WIP ultimately subject to this list: https://ofac.treasury.gov/sanctions-programs-and-country-information
96-
// From lawyers: north korea, cuba, iran, ukraine, syria
97-
export const SANCTIONED_COUNTRY_CODES = ['KP', 'CU', 'IR', 'UA', 'SY']
95+
export const SANCTIONED_COUNTRY_CODES = process.env.SANCTIONED_COUNTRY_CODES?.split(',') || []
9896

9997
export const TERRITORY_COST_MONTHLY = 50000
10098
export const TERRITORY_COST_YEARLY = 500000

next.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ module.exports = withPlausibleProxy()({
220220
'process.env.NEXT_PUBLIC_NORMAL_POLL_INTERVAL': JSON.stringify(process.env.NEXT_PUBLIC_NORMAL_POLL_INTERVAL),
221221
'process.env.NEXT_PUBLIC_LONG_POLL_INTERVAL': JSON.stringify(process.env.NEXT_PUBLIC_LONG_POLL_INTERVAL),
222222
'process.env.NEXT_PUBLIC_EXTRA_LONG_POLL_INTERVAL': JSON.stringify(process.env.NEXT_PUBLIC_EXTRA_LONG_POLL_INTERVAL),
223+
'process.env.SANCTIONED_COUNTRY_CODES': JSON.stringify(process.env.SANCTIONED_COUNTRY_CODES),
223224
'process.env.NEXT_IS_EXPORT_WORKER': 'true'
224225
})
225226
]

pages/api/auth/[...nextauth].js

+3-6
Original file line numberDiff line numberDiff line change
@@ -125,15 +125,12 @@ function getCallbacks (req, res) {
125125
token.sub = Number(token.id)
126126
}
127127

128-
// this only runs during a signup/login because response is only defined during signup/login
129-
// and will add the multi_auth cookies for the user we just logged in as
130-
if (req && res) {
131-
req = new NodeNextRequest(req)
132-
res = new NodeNextResponse(res)
128+
// add multi_auth cookie for user that just logged in
129+
if (user && req && res) {
133130
const secret = process.env.NEXTAUTH_SECRET
134131
const jwt = await encodeJWT({ token, secret })
135132
const me = await prisma.user.findUnique({ where: { id: token.id } })
136-
setMultiAuthCookies(req, res, { ...me, jwt })
133+
setMultiAuthCookies(new NodeNextRequest(req), new NodeNextResponse(res), { ...me, jwt })
137134
}
138135

139136
return token

pages/api/lnurlp/[username]/pay.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,6 @@ export default async ({ query: { username, amount, nostr, comment, payerdata: pa
9696
})
9797
} catch (error) {
9898
console.log(error)
99-
res.status(400).json({ status: 'ERROR', reason: 'could not generate invoice' })
99+
res.status(400).json({ status: 'ERROR', reason: 'could not generate invoice to customer\'s attached wallet' })
100100
}
101101
}

0 commit comments

Comments
 (0)