mirror of
https://github.com/aljazceru/landscape-template.git
synced 2025-12-17 06:14:27 +01:00
feat: create an api for signing events
This commit is contained in:
@@ -1,29 +1,13 @@
|
||||
const { ApolloServer } = require("apollo-server-lambda");
|
||||
const schema = require('./schema')
|
||||
const cookie = require('cookie')
|
||||
const jose = require('jose');
|
||||
const { JWT_SECRET } = require("../../utils/consts");
|
||||
const extractKeyFromCookie = require("../../utils/extractKeyFromCookie");
|
||||
|
||||
|
||||
const extractKey = async (cookieHeader) => {
|
||||
const cookies = cookie.parse(cookieHeader ?? '');
|
||||
const token = cookies.Authorization;
|
||||
if (token) {
|
||||
try {
|
||||
const { payload } = await jose.jwtVerify(token, Buffer.from(JWT_SECRET), {
|
||||
algorithms: ['HS256'],
|
||||
})
|
||||
return payload.pubKey
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const server = new ApolloServer({
|
||||
schema,
|
||||
context: async ({ event }) => {
|
||||
const userPubKey = await extractKey(event.headers.cookie ?? event.headers.Cookie)
|
||||
const userPubKey = await extractKeyFromCookie(event.headers.cookie ?? event.headers.Cookie)
|
||||
return { userPubKey }
|
||||
},
|
||||
});
|
||||
|
||||
74
api/functions/sign-event/sign-event.js
Normal file
74
api/functions/sign-event/sign-event.js
Normal file
@@ -0,0 +1,74 @@
|
||||
|
||||
const serverless = require('serverless-http');
|
||||
const { createExpressApp } = require('../../modules');
|
||||
const express = require('express');
|
||||
const extractKeyFromCookie = require('../../utils/extractKeyFromCookie');
|
||||
const { getUserByPubKey } = require('../../auth/utils/helperFuncs');
|
||||
const { generatePrivateKey, getPublicKey, signEvent: signNostrEvent } = require('nostr-tools');
|
||||
const { prisma } = require('../../prisma');
|
||||
|
||||
|
||||
const signEvent = async (req, res) => {
|
||||
try {
|
||||
const userPubKey = await extractKeyFromCookie(req.headers.cookie ?? req.headers.Cookie)
|
||||
const user = await getUserByPubKey(userPubKey);
|
||||
|
||||
if (!user)
|
||||
return res.status(401).json({ status: 'ERROR', reason: 'Not Authenticated' });
|
||||
|
||||
let prvkey = user.nostr_prv_key, pubkey = user.nostr_pub_key;
|
||||
|
||||
if (!prvkey) {
|
||||
prvkey = generatePrivateKey();
|
||||
pubkey = getPublicKey(prvkey);
|
||||
|
||||
await prisma.user.update({
|
||||
where: {
|
||||
id: user.id,
|
||||
},
|
||||
data: {
|
||||
nostr_prv_key: prvkey,
|
||||
nostr_pub_key: pubkey
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const { content, tags } = req.body
|
||||
const event = {
|
||||
kind: 1,
|
||||
pubkey,
|
||||
content,
|
||||
tags,
|
||||
created_at: Date.now(),
|
||||
}
|
||||
|
||||
event.sig = await signNostrEvent(event, prvkey)
|
||||
|
||||
return res
|
||||
.status(200)
|
||||
.json({ event });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
res.status(500).send("Unexpected error happened, please try again")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
let app;
|
||||
|
||||
if (process.env.LOCAL) {
|
||||
app = createExpressApp()
|
||||
app.get('/sign-event', signEvent);
|
||||
}
|
||||
else {
|
||||
const router = express.Router();
|
||||
router.get('/sign-event', signEvent)
|
||||
app = createExpressApp(router)
|
||||
}
|
||||
|
||||
|
||||
const handler = serverless(app);
|
||||
exports.handler = async (event, context) => {
|
||||
return await handler(event, context);
|
||||
};
|
||||
22
api/utils/extractKeyFromCookie.js
Normal file
22
api/utils/extractKeyFromCookie.js
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
const cookie = require('cookie')
|
||||
const jose = require('jose');
|
||||
const { JWT_SECRET } = require("./consts");
|
||||
|
||||
const extractKeyFromCookie = async (cookieHeader) => {
|
||||
const cookies = cookie.parse(cookieHeader ?? '');
|
||||
const token = cookies.Authorization;
|
||||
if (token) {
|
||||
try {
|
||||
const { payload } = await jose.jwtVerify(token, Buffer.from(JWT_SECRET), {
|
||||
algorithms: ['HS256'],
|
||||
})
|
||||
return payload.pubKey
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = extractKeyFromCookie;
|
||||
@@ -0,0 +1,6 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Question" ADD COLUMN "is_published" BOOLEAN NOT NULL DEFAULT true;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "User" ADD COLUMN "nostr_prv_key" TEXT,
|
||||
ADD COLUMN "nostr_pub_key" TEXT;
|
||||
@@ -56,7 +56,8 @@ model User {
|
||||
linkedin String?
|
||||
bio String?
|
||||
location String?
|
||||
|
||||
nostr_prv_key String?
|
||||
nostr_pub_key String?
|
||||
|
||||
join_date DateTime @default(now())
|
||||
|
||||
@@ -134,13 +135,13 @@ model Story {
|
||||
}
|
||||
|
||||
model Question {
|
||||
id Int @id @default(autoincrement())
|
||||
title String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
body String
|
||||
excerpt String
|
||||
votes_count Int @default(0)
|
||||
id Int @id @default(autoincrement())
|
||||
title String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
body String
|
||||
excerpt String
|
||||
votes_count Int @default(0)
|
||||
is_published Boolean @default(true)
|
||||
|
||||
|
||||
|
||||
@@ -48,3 +48,9 @@ functions:
|
||||
- http:
|
||||
path: is-logged-in
|
||||
method: get
|
||||
sign-event:
|
||||
handler: api/functions/sign-event/sign-event.handler
|
||||
events:
|
||||
- http:
|
||||
path: sign-event
|
||||
method: post
|
||||
|
||||
@@ -1,26 +1,3 @@
|
||||
import { signEvent } from "nostr-tools";
|
||||
|
||||
|
||||
const prvkey = '3e345e14bbf758be3ccdca67cab6c808d0a7f001433cf2492b3c2c93327ebde8';
|
||||
const pubkey = 'f183af77e76bd62f0af8820ffa6d708d6826cf4a2adaa6be2d49061c94e22c0b';
|
||||
|
||||
|
||||
export async function signEventByServer(_event) {
|
||||
// Extract user Id from cookie
|
||||
// Get his prvkey & pubkey from database
|
||||
// Generate a new prvkey if not exist & store it in the DB
|
||||
|
||||
const event = {
|
||||
..._event,
|
||||
pubkey,
|
||||
tags: _event.tags ?? []
|
||||
};
|
||||
|
||||
if (!event.sig)
|
||||
event.sig = await signEvent(event, prvkey)
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
export async function mapPubkeysToUsers(pubkeys) {
|
||||
const users = await Promise.all(pubkeys.map(pubkey =>
|
||||
|
||||
@@ -2,7 +2,8 @@ import dayjs from 'dayjs'
|
||||
|
||||
import { relayPool } from 'nostr-tools'
|
||||
import { Nullable } from 'remirror';
|
||||
import { mapPubkeysToUsers, signEventByServer } from './comment.server';
|
||||
import { CONSTS } from 'src/utils';
|
||||
import { mapPubkeysToUsers, } from './comment.server';
|
||||
|
||||
|
||||
const pool = relayPool()
|
||||
@@ -50,6 +51,14 @@ export function sub(filter: any) {
|
||||
};
|
||||
}
|
||||
|
||||
const getSignedEvents = async (event: any) => {
|
||||
const res = await fetch(CONSTS.apiEndpoint + '/sign-event', {
|
||||
credentials: 'include'
|
||||
})
|
||||
const data = await res.json()
|
||||
return data.event;
|
||||
}
|
||||
|
||||
export async function post(data: string, filter: any) {
|
||||
|
||||
|
||||
@@ -60,7 +69,7 @@ export async function post(data: string, filter: any) {
|
||||
content: data
|
||||
};
|
||||
|
||||
event = await signEventByServer(event);
|
||||
event = await getSignedEvents(event);
|
||||
const publishTimeout = setTimeout(() => {
|
||||
alert(
|
||||
`failed to publish event ${event.id?.slice(0, 5)}… to any relay.`
|
||||
|
||||
Reference in New Issue
Block a user