29 Commits

Author SHA1 Message Date
5dc10a4d33 Merge branch 'master' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-14 16:46:27 +02:00
2297c68046 Merge branch 'master' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-14 16:41:01 +02:00
b82ab45c99 Merge branch 'master' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-14 14:57:35 +02:00
d12c63db26 Merge branch 'master' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-10 16:07:24 +02:00
e6a9ef84ce Merge branch 'master' into live
Some checks failed
continuous-integration/drone/push Build is failing
2024-08-19 15:13:46 +02:00
b7e91344a0 Merge branch 'master' into live
Some checks failed
continuous-integration/drone/push Build is failing
2024-08-19 14:48:35 +02:00
0f07e32781 Merge branch 'master' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-17 14:49:29 +02:00
1311b5ed6a Merge branch 'master' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-17 14:46:00 +02:00
12f82061e8 Merge branch 'master' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-17 14:42:32 +02:00
a07b4369ab Hide njump link for users without pubkey
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-23 17:33:34 +02:00
2605c06807 Merge branch 'feature/admin_pages' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-23 17:30:22 +02:00
1db768fb15 Merge branch 'feature/admin_pages' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-23 17:27:03 +02:00
8a7403df32 Merge branch 'feature/own_relay' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-20 15:52:03 +02:00
f0295fef7a Merge branch 'feature/own_relay' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-20 15:28:55 +02:00
090affd304 Merge branch 'feature/own_relay' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-20 14:51:20 +02:00
bafddd436b Merge branch 'feature/own_relay' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-20 13:59:59 +02:00
560f193c4b Merge branch 'master' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-20 13:56:11 +02:00
8aabbad5bb Merge branch 'feature/strfry_zap_receipts' into live 2024-06-20 13:56:00 +02:00
ba8d21eb7a Merge branch 'master' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-09 13:29:57 +02:00
53df455d53 Merge branch 'master' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-09 13:17:51 +02:00
9f1af3a9aa Merge branch 'master' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-07 14:13:53 +02:00
1d09008ce2 Merge branch 'master' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-04 17:45:13 +02:00
57c5317c38 Merge branch 'feature/170-nostr_zaps' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-21 18:29:13 +02:00
41bd920060 Merge branch 'feature/170-nostr_zaps' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-21 18:09:09 +02:00
0815fa6040 Merge branch 'feature/170-nostr_zaps' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-19 17:07:58 +02:00
af0e99aa50 Merge branch 'feature/170-nostr_zaps' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-19 16:55:10 +02:00
f05eec5255 Merge branch 'feature/170-nostr_zaps' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-19 16:48:28 +02:00
66ca2dc6b0 Merge branch 'feature/173-nostr_ldap' into live
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-19 13:44:24 +02:00
800183e9da Increase sidekiq concurrency in prod
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-19 13:44:01 +02:00
10 changed files with 45 additions and 12005 deletions

View File

@@ -74,7 +74,7 @@ class WebfingerController < WellKnownController
end end
def remotestorage_link def remotestorage_link
auth_url = new_rs_oauth_url(@username, host: Setting.accounts_domain) auth_url = new_rs_oauth_url(@username)
storage_url = "#{Setting.rs_storage_url}/#{@username}" storage_url = "#{Setting.rs_storage_url}/#{@username}"
{ {

View File

@@ -1,14 +1,8 @@
import { Controller } from "@hotwired/stimulus" import { Controller } from "@hotwired/stimulus"
import { Nostrify } from "nostrify"
// Connects to data-controller="settings--nostr-pubkey" // Connects to data-controller="settings--nostr-pubkey"
export default class extends Controller { export default class extends Controller {
static targets = [ static targets = [ "noExtension", "setPubkey", "pubkeyBech32Input" ]
"noExtension",
"setPubkey", "pubkeyBech32Input",
"relayList", "relayListStatus",
"profileStatusNip05", "profileStatusLud16"
]
static values = { static values = {
userAddress: String, userAddress: String,
pubkeyHex: String, pubkeyHex: String,
@@ -21,14 +15,6 @@ export default class extends Controller {
if (this.hasSetPubkeyTarget) { if (this.hasSetPubkeyTarget) {
this.setPubkeyTarget.disabled = false this.setPubkeyTarget.disabled = false
} }
if (this.pubkeyHexValue) {
this.discoverUserOnNostr().then(() => {
this.renderRelayStatus()
this.renderProfileNip05Status()
this.renderProfileLud16Status()
})
}
} else { } else {
this.noExtensionTarget.classList.remove("hidden") this.noExtensionTarget.classList.remove("hidden")
} }
@@ -63,172 +49,8 @@ export default class extends Controller {
} }
} }
async discoverUserOnNostr () {
this.nip65Relays = await this.findUserRelays()
this.profile = await this.findUserProfile()
}
async findUserRelays () {
const controller = new AbortController();
const signal = controller.signal;
const filters = [{ kinds: [10002], authors: [this.pubkeyHexValue], limit: 1 }]
const messages = []
for await (const msg of this.discoveryPool.req(filters, { signal })) {
if (msg[0] === 'EVENT') {
if (!messages.find(m => m.id === msg[2].id)) {
messages.push(msg[2])
}
}
if (msg[0] === 'EOSE') { break }
}
// Close the relay subscription
controller.abort()
if (messages.length === 0) { return messages }
const sortedMessages = messages.sort((a, b) => a.createdAt - b.createdAt)
const newestMessage = messages[messages.length - 1]
return newestMessage.tags.filter(t => t[0] === 'r')
.map(t => { return { url: t[1], marker: t[2] } })
}
async findUserProfile () {
const controller = new AbortController();
const signal = controller.signal;
const filters = [{ kinds: [0], authors: [this.pubkeyHexValue], limit: 1 }]
const messages = []
for await (const msg of this.discoveryPool.req(filters, { signal })) {
if (msg[0] === 'EVENT') {
if (!messages.find(m => m.id === msg[2].id)) {
messages.push(msg[2])
}
}
if (msg[0] === 'EOSE') { break }
}
// Close the relay subscription
controller.abort()
if (messages.length === 0) { return null }
const sortedMessages = messages.sort((a, b) => a.createdAt - b.createdAt)
const newestMessage = messages[messages.length - 1]
return JSON.parse(newestMessage.content)
}
renderRelayStatus () {
let showStatus
if (this.nip65Relays.length > 0) {
if (this.relaysContainAccountsRelay) {
showStatus = 'green'
} else {
showStatus = 'orange'
}
} else {
showStatus = 'red'
}
// showStatus = 'red'
this.relayListStatusTarget
.querySelector(`.status-${showStatus}`)
.classList.remove("hidden")
}
renderProfileNip05Status () {
let showStatus
if (this.profile?.nip05) {
if (this.profile.nip05 === this.userAddressValue) {
showStatus = 'green'
} else {
showStatus = 'red'
}
} else {
showStatus = 'orange'
}
this.profileStatusNip05Target
.querySelector(`.status-${showStatus}`)
.classList.remove("hidden")
}
renderProfileLud16Status () {
let showStatus
if (this.profile?.lud16) {
if (this.profile.lud16 === this.userAddressValue) {
showStatus = 'green'
} else {
showStatus = 'red'
}
} else {
showStatus = 'orange'
}
this.profileStatusLud16Target
.querySelector(`.status-${showStatus}`)
.classList.remove("hidden")
}
// renderRelayList (relays) {
// const html = relays.map(relay => `
// <li class="flex items-center justify-between p-2 border-b">
// <span>${relay.url}</span>
// <button
// data-action="click->list#handleItemClick"
// data-item="${relay.url}"
// class="bg-blue-500 text-white px-3 py-1 rounded">
// Action
// </button>
// </li>
// `).join("")
//
// this.relayListTarget.innerHTML = html
// }
get csrfToken () { get csrfToken () {
const element = document.head.querySelector('meta[name="csrf-token"]') const element = document.head.querySelector('meta[name="csrf-token"]')
return element.getAttribute("content") return element.getAttribute("content")
} }
// Used to find a user's profile and relays
get discoveryRelays () {
return [
'ws://localhost:4777',
'wss://nostr.kosmos.org',
'wss://purplepag.es',
// 'wss://relay.nostr.band',
// 'wss://njump.me',
// 'wss://relay.damus.io',
// 'wss://nos.lol',
// 'wss://eden.nostr.land',
// 'wss://relay.snort.social',
// 'wss://nostr.wine',
// 'wss://relay.primal.net',
// 'wss://nostr.bitcoiner.social',
]
}
get discoveryPool () {
if (!this._discoveryPool) {
this._discoveryPool = new Nostrify.NPool({
open: (url) => new Nostrify.NRelay1(url),
reqRouter: async (filters) => new Map(
this.discoveryRelays.map(relayUrl => [ relayUrl, filters ])
),
eventRouter: async (event) => [],
})
}
return this._discoveryPool
}
get relaysContainAccountsRelay () {
// TODO use URL from view/settings
return !!this.nip65Relays.find(r => r.url.match('wss://nostr.kosmos.org'))
}
} }

View File

@@ -184,7 +184,7 @@
<td>XMPP (ejabberd)</td> <td>XMPP (ejabberd)</td>
<td> <td>
<%= render FormElements::ToggleComponent.new( <%= render FormElements::ToggleComponent.new(
enabled: @services_enabled.include?("ejabberd"), enabled: @services_enabled.include?("xmpp"),
input_enabled: false input_enabled: false
) %> ) %>
</td> </td>
@@ -205,7 +205,9 @@
) %> ) %>
</td> </td>
<td class="text-right"> <td class="text-right">
<% if @user.nostr_pubkey.present? %>
<%= link_to "Open profile", "https://njump.me/#{@user.nostr_pubkey_bech32}", class: "btn-sm btn-gray" %> <%= link_to "Open profile", "https://njump.me/#{@user.nostr_pubkey_bech32}", class: "btn-sm btn-gray" %>
<% end %>
</td> </td>
</tr> </tr>
<% end %> <% end %>

View File

@@ -1,32 +1,46 @@
<div data-controller="settings--nostr-pubkey" <section>
data-settings--nostr-pubkey-user-address-value="<%= current_user.address %>" <h3>Nostr</h3>
data-settings--nostr-pubkey-site-value="<%= Setting.accounts_domain %>" <h4 class="mb-0">Public Key</h4>
data-settings--nostr-pubkey-shared-secret-value="<%= session[:shared_secret] %>" <div data-controller="settings--nostr-pubkey"
data-settings--nostr-pubkey-pubkey-hex-value="<%= current_user.nostr_pubkey %>"> data-settings--nostr-pubkey-user-address-value="<%= current_user.address %>"
<section> data-settings--nostr-pubkey-site-value="<%= Setting.accounts_domain %>"
<h3>Nostr</h3> data-settings--nostr-pubkey-shared-secret-value="<%= session[:shared_secret] %>"
<h4 class="mb-0"> data-settings--nostr-pubkey-pubkey-hex-value="<%= current_user.nostr_pubkey %>">
Public Key
</h4> <p class="<%= current_user.nostr_pubkey.present? ? '' : 'hidden' %> mt-2 flex gap-1">
<p class="<%= current_user.nostr_pubkey.present? ? '' : 'hidden' %> mt-2 flex gap-x-1">
<input type="text" value="<%= current_user.nostr_pubkey_bech32 %>" disabled <input type="text" value="<%= current_user.nostr_pubkey_bech32 %>" disabled
data-settings--nostr-pubkey-target="pubkeyBech32Input" data-settings--nostr-pubkey-target="pubkeyBech32Input"
name="nostr_public_key" class="w-full" /> name="nostr_public_key" class="relative grow" />
<%= link_to nostr_pubkey_settings_path, <%= link_to nostr_pubkey_settings_path,
class: 'btn-md btn-outline relative grow-0 shrink-0 text-red-700', class: 'btn-md btn-outline text-red-700 relative shrink-0',
data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' } do %> data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' } do %>
Remove Remove
<% end %> <% end %>
</p> </p>
<% if current_user.nostr_pubkey.present? %> <% if current_user.nostr_pubkey.present? %>
<!-- <div> --> <div class="rounded-md bg-blue-50 p-4">
<!-- Pubkey present --> <div class="flex">
<!-- </div> --> <div class="flex-shrink-0">
<svg class="h-5 w-5 text-blue-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3 flex-1">
<p class="text-sm text-blue-800">
Your user address <strong><%= current_user.address %></strong> is
also a Nostr address now. Use your favorite Nostr app, or for
example <a href="http://metadata.nostr.com" target="_blank"
class="underline">metadata.nostr.com</a>, to add this
<strong>NIP-05</strong> address to your public profile.
</p>
</div>
</div>
</div>
<% else %> <% else %>
<p class="my-4"> <p class="my-4">
Verify your Nostr public key with us in order to enable Nostr-specific If you use any apps on the Nostr network, you can verify your public key
features for your account. with us in order to enable Nostr-specific features for your account.
</p> </p>
<% end %> <% end %>
@@ -44,8 +58,8 @@
</h3> </h3>
<div class="mt-2 mb-0 text-sm text-blue-800"> <div class="mt-2 mb-0 text-sm text-blue-800">
<p> <p>
We recommend Alby, which you can also use a wallet for your We recommend Alby, which you can also use for your Lightning
Lightning account. Wallet.
</p> </p>
</div> </div>
<div class="mt-4"> <div class="mt-4">
@@ -72,113 +86,5 @@
</button> </button>
</p> </p>
<% end %> <% end %>
</section> </div>
</section>
<% if current_user.nostr_pubkey.present? %>
<section>
<h3>Profile</h3>
<div data-settings--nostr-pubkey-target="profileStatus" class="mb-4">
<p class="status-green hidden flex gap-x-4 items-center">
<span class="inline-block h-6 w-6 grow-0 text-emerald-500 %>">
<%= render "icons/check-circle" %>
</span>
<span>
You already have a profile for your public key
</span>
</p>
<p class="status-orange hidden flex gap-x-4 items-center">
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
<%= render "icons/alert-octagon" %>
</span>
<span>
<strong><%= current_user.address %></strong> is not set as your Nostr address
</span>
</p>
</div>
<div data-settings--nostr-pubkey-target="profileStatusNip05" class="mb-4">
<p class="status-green hidden flex gap-x-4 items-center">
<span class="inline-block h-6 w-6 grow-0 text-emerald-500 %>">
<%= render "icons/check-circle" %>
</span>
<span>
<strong><%= current_user.address %></strong> is set as your Nostr address
</span>
</p>
<p class="status-orange hidden flex gap-x-4 items-center">
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
<%= render "icons/alert-octagon" %>
</span>
<span>
<strong><%= current_user.address %></strong> is not set as your Nostr address
</span>
</p>
<p class="status-red hidden flex gap-x-4 items-center">
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
<%= render "icons/alert-octagon" %>
</span>
<span>
Your profile's Nostr address is not set to <strong><%= current_user.address %></strong> yet
</span>
</p>
</div>
<div data-settings--nostr-pubkey-target="profileStatusLud16">
<p class="status-green hidden flex gap-x-4 items-center">
<span class="inline-block h-6 w-6 grow-0 text-emerald-500 %>">
<%= render "icons/check-circle" %>
</span>
<span>
<strong><%= current_user.address %></strong> is set as your Lightning address
</span>
</p>
<p class="status-orange hidden flex gap-x-4 items-center">
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
<%= render "icons/alert-octagon" %>
</span>
<span>
<strong><%= current_user.address %></strong> is not set as your Lightning address yet
</span>
</p>
<p class="status-red hidden flex gap-x-4 items-center">
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
<%= render "icons/alert-octagon" %>
</span>
<span>
Your profile's Lightning address is not set to <strong><%= current_user.address %></strong> yet
</span>
</p>
</div>
</section>
<section>
<h3>Relays</h3>
<div data-settings--nostr-pubkey-target="relayListStatus">
<p class="status-green hidden flex gap-x-4 items-center">
<span class="inline-block h-6 w-6 grow-0 text-emerald-500 %>">
<%= render "icons/check-circle" %>
</span>
<span>
You have a relay list, and the Kosmos relay is part of it
</span>
</p>
<p class="status-orange hidden flex gap-x-4 items-center">
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
<%= render "icons/alert-octagon" %>
</span>
<span>
The Kosmos relay is missing from your relay list
</span>
</p>
<p class="status-red hidden flex gap-x-4 items-center">
<span class="inline-block h-6 w-6 grow-0 text-amber-500 %>">
<%= render "icons/alert-octagon" %>
</span>
<span>
We could not find a relay list for your public key
</span>
</p>
</div>
<ul data-settings--nostr-pubkey-target="relayList">
</ul>
</section>
<% end %>
</div>

View File

@@ -6,4 +6,3 @@ pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin_all_from "app/javascript/controllers", under: "controllers" pin_all_from "app/javascript/controllers", under: "controllers"
pin "tailwindcss-stimulus-components" # @4.0.3 pin "tailwindcss-stimulus-components" # @4.0.3
pin "nostrify"

View File

@@ -1,4 +1,6 @@
:concurrency: 2 :concurrency: 2
production:
:concurrency: 10
:queues: :queues:
- default - default
- mailers - mailers

View File

@@ -11,7 +11,7 @@
"postcss-preset-env": "^7.8.3", "postcss-preset-env": "^7.8.3",
"tailwindcss": "^3.2.4" "tailwindcss": "^3.2.4"
}, },
"version": "0.10.0", "version": "0.9.0",
"scripts": { "scripts": {
"build:css:tailwind": "tailwindcss --postcss -i ./app/assets/stylesheets/application.tailwind.css -o ./app/assets/builds/application.css", "build:css:tailwind": "tailwindcss --postcss -i ./app/assets/stylesheets/application.tailwind.css -o ./app/assets/builds/application.css",
"build:css": "yarn run build:css:tailwind" "build:css": "yarn run build:css:tailwind"

View File

@@ -92,7 +92,7 @@ RSpec.describe "WebFinger", type: :request do
expect(rs_link["href"]).to eql("#{Setting.rs_storage_url}/tony") expect(rs_link["href"]).to eql("#{Setting.rs_storage_url}/tony")
oauth_url = rs_link["properties"]["http://tools.ietf.org/html/rfc6749#section-4.2"] oauth_url = rs_link["properties"]["http://tools.ietf.org/html/rfc6749#section-4.2"]
expect(oauth_url).to eql("http://accounts.kosmos.org/rs/oauth/tony") expect(oauth_url).to eql("http://www.example.com/rs/oauth/tony")
end end
it "returns CORS headers" do it "returns CORS headers" do

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long