format project
This commit is contained in:
parent
90d8e39eb3
commit
c02cf8c12e
39 changed files with 2062 additions and 909 deletions
19
.eslintrc.json
Normal file
19
.eslintrc.json
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"node": true,
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
|
||||||
|
"rules": {
|
||||||
|
"prettier/prettier": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"semi": false,
|
||||||
|
"trailingComma": "es5"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-console": "warn"
|
||||||
|
}
|
||||||
|
}
|
||||||
8
.prettierrc
Normal file
8
.prettierrc
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
"prettier-plugin-ejs"
|
||||||
|
],
|
||||||
|
"singleQuote": true,
|
||||||
|
"semi": true,
|
||||||
|
"trailingComma": "es5"
|
||||||
|
}
|
||||||
9
eslint.config.mjs
Normal file
9
eslint.config.mjs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import globals from 'globals'
|
||||||
|
import pluginJs from '@eslint/js'
|
||||||
|
|
||||||
|
/** @type {import('eslint').Linter.Config[]} */
|
||||||
|
export default [
|
||||||
|
{ files: ['**/*.js'], languageOptions: { sourceType: 'commonjs' } },
|
||||||
|
{ languageOptions: { globals: { ...globals.browser, ...globals.node } } },
|
||||||
|
pluginJs.configs.recommended,
|
||||||
|
]
|
||||||
1041
package-lock.json
generated
1041
package-lock.json
generated
File diff suppressed because it is too large
Load diff
15
package.json
15
package.json
|
|
@ -20,9 +20,20 @@
|
||||||
"start:containers": "docker compose up -d --build",
|
"start:containers": "docker compose up -d --build",
|
||||||
"stop:containers": "docker compose down",
|
"stop:containers": "docker compose down",
|
||||||
"cli": "node src/cli.js",
|
"cli": "node src/cli.js",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"lint": "eslint . --fix",
|
||||||
|
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,scss,html,ejs}\""
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC"
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.20.0",
|
||||||
|
"eslint": "^9.20.1",
|
||||||
|
"eslint-config-prettier": "^10.0.1",
|
||||||
|
"eslint-plugin-prettier": "^5.2.3",
|
||||||
|
"globals": "^15.15.0",
|
||||||
|
"prettier": "^3.5.1",
|
||||||
|
"prettier-plugin-ejs": "^1.0.3"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,7 @@ const program = new Command();
|
||||||
|
|
||||||
const createAppInviteCommand = require('./commands/createAppInvite');
|
const createAppInviteCommand = require('./commands/createAppInvite');
|
||||||
|
|
||||||
program
|
program.version('1.0.0').description('CLI for managing web app tasks');
|
||||||
.version('1.0.0')
|
|
||||||
.description('CLI for managing web app tasks');
|
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('createAppInvite <inviterNpub>')
|
.command('createAppInvite <inviterNpub>')
|
||||||
|
|
|
||||||
|
|
@ -3,5 +3,5 @@ const DEFAULT_NOSTR_CHALLENGE_DURATION_SECONDS = 60 * 60 * 24 * 30;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
DEFAULT_SESSION_DURATION_SECONDS,
|
DEFAULT_SESSION_DURATION_SECONDS,
|
||||||
DEFAULT_NOSTR_CHALLENGE_DURATION_SECONDS
|
DEFAULT_NOSTR_CHALLENGE_DURATION_SECONDS,
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,17 @@ const sequelize = new Sequelize({
|
||||||
timestamps: false,
|
timestamps: false,
|
||||||
freezeTableName: true,
|
freezeTableName: true,
|
||||||
underscored: true,
|
underscored: true,
|
||||||
quoteIdentifiers: false
|
quoteIdentifiers: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
sequelize.sync().then(() => {
|
sequelize
|
||||||
|
.sync()
|
||||||
|
.then(() => {
|
||||||
console.log('Database synced');
|
console.log('Database synced');
|
||||||
}).catch(err => {
|
})
|
||||||
|
.catch((err) => {
|
||||||
console.error('Error syncing the database:', err);
|
console.error('Error syncing the database:', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = sequelize;
|
module.exports = sequelize;
|
||||||
|
|
@ -1,21 +1,21 @@
|
||||||
class AlreadyUsedError extends Error {
|
class AlreadyUsedError extends Error {
|
||||||
constructor(message) {
|
constructor(message) {
|
||||||
super(message);
|
super(message);
|
||||||
this.name = "AlreadyUsedError"
|
this.name = 'AlreadyUsedError';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InvalidSignatureError extends Error {
|
class InvalidSignatureError extends Error {
|
||||||
constructor(message) {
|
constructor(message) {
|
||||||
super(message);
|
super(message);
|
||||||
this.name = "InvalidSignatureError";
|
this.name = 'InvalidSignatureError';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NotFoundError extends Error {
|
class NotFoundError extends Error {
|
||||||
constructor(message) {
|
constructor(message) {
|
||||||
super(message);
|
super(message);
|
||||||
this.name = "AppInvitedUsedError";
|
this.name = 'AppInvitedUsedError';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,5 +30,5 @@ module.exports = {
|
||||||
AlreadyUsedError,
|
AlreadyUsedError,
|
||||||
InvalidSignatureError,
|
InvalidSignatureError,
|
||||||
NotFoundError,
|
NotFoundError,
|
||||||
ExpiredError
|
ExpiredError,
|
||||||
};
|
};
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
const sessionService = require('../services/sessionService');
|
const sessionService = require('../services/sessionService');
|
||||||
|
|
||||||
async function attachPublicKeyMiddleware(req, res, next) {
|
async function attachPublicKeyMiddleware(req, res, next) {
|
||||||
|
|
||||||
const publicKey = await sessionService.getPublicKeyRelatedToSession(
|
const publicKey = await sessionService.getPublicKeyRelatedToSession(
|
||||||
req.cookies.sessionUuid
|
req.cookies.sessionUuid
|
||||||
)
|
);
|
||||||
req.cookies.publicKey = publicKey;
|
req.cookies.publicKey = publicKey;
|
||||||
|
|
||||||
next();
|
next();
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ async function rejectIfNotAuthorizedMiddleware(req, res, next) {
|
||||||
if (!(await sessionService.isSessionAuthorized(req.cookies.sessionUuid))) {
|
if (!(await sessionService.isSessionAuthorized(req.cookies.sessionUuid))) {
|
||||||
return res.status(403).json({
|
return res.status(403).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Your session is not authorized.'
|
message: 'Your session is not authorized.',
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,23 @@
|
||||||
const uuid = require("uuid");
|
const uuid = require('uuid');
|
||||||
|
|
||||||
const sessionService = require('../services/sessionService');
|
const sessionService = require('../services/sessionService');
|
||||||
const constants = require('../constants');
|
const constants = require('../constants');
|
||||||
|
|
||||||
async function setAndPersistNewSession(res) {
|
async function setAndPersistNewSession(res) {
|
||||||
const sessionUuid = uuid.v7();
|
const sessionUuid = uuid.v7();
|
||||||
res.cookie('sessionUuid', sessionUuid, { httpOnly: true, maxAge: constants.DEFAULT_SESSION_DURATION_SECONDS * 1000 });
|
res.cookie('sessionUuid', sessionUuid, {
|
||||||
|
httpOnly: true,
|
||||||
|
maxAge: constants.DEFAULT_SESSION_DURATION_SECONDS * 1000,
|
||||||
|
});
|
||||||
return await sessionService.createSession(sessionUuid);
|
return await sessionService.createSession(sessionUuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createSessionMiddleware(req, res, next) {
|
async function createSessionMiddleware(req, res, next) {
|
||||||
|
|
||||||
const sessionUuid = req.cookies.sessionUuid;
|
const sessionUuid = req.cookies.sessionUuid;
|
||||||
|
|
||||||
if (!sessionUuid) {
|
if (!sessionUuid) {
|
||||||
const newSession = await setAndPersistNewSession(res);
|
const newSession = await setAndPersistNewSession(res);
|
||||||
req.cookies.sessionUuid = newSession.uuid;
|
req.cookies.sessionUuid = newSession.uuid;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sessionUuid) {
|
if (sessionUuid) {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
const { DataTypes } = require('sequelize');
|
const { DataTypes } = require('sequelize');
|
||||||
const sequelize = require('../database');
|
const sequelize = require('../database');
|
||||||
|
|
||||||
const AppInviteCreated = sequelize.define('AppInviteCreated', {
|
const AppInviteCreated = sequelize.define(
|
||||||
|
'AppInviteCreated',
|
||||||
|
{
|
||||||
uuid: {
|
uuid: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
unique: true,
|
unique: true,
|
||||||
primaryKey: true
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
inviter_pub_key: {
|
inviter_pub_key: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
|
|
@ -14,10 +16,12 @@ const AppInviteCreated = sequelize.define('AppInviteCreated', {
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tableName: 'app_invite_created',
|
||||||
}
|
}
|
||||||
}, {
|
);
|
||||||
tableName: 'app_invite_created'
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = AppInviteCreated;
|
module.exports = AppInviteCreated;
|
||||||
|
|
@ -1,27 +1,31 @@
|
||||||
const { DataTypes } = require('sequelize');
|
const { DataTypes } = require('sequelize');
|
||||||
const sequelize = require('../database');
|
const sequelize = require('../database');
|
||||||
|
|
||||||
const ContactDetailsSet = sequelize.define('ContactDetailsSet', {
|
const ContactDetailsSet = sequelize.define(
|
||||||
|
'ContactDetailsSet',
|
||||||
|
{
|
||||||
uuid: {
|
uuid: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
unique: true,
|
unique: true,
|
||||||
primaryKey: true
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
public_key: {
|
public_key: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
},
|
},
|
||||||
encrypted_contact_details: {
|
encrypted_contact_details: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tableName: 'contact_details_set',
|
||||||
}
|
}
|
||||||
}, {
|
);
|
||||||
tableName: 'contact_details_set'
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = ContactDetailsSet;
|
module.exports = ContactDetailsSet;
|
||||||
|
|
@ -1,31 +1,35 @@
|
||||||
const { DataTypes } = require('sequelize');
|
const { DataTypes } = require('sequelize');
|
||||||
const sequelize = require('../database');
|
const sequelize = require('../database');
|
||||||
|
|
||||||
const NostrChallengeCompleted = sequelize.define('NostrChallengeCompleted', {
|
const NostrChallengeCompleted = sequelize.define(
|
||||||
|
'NostrChallengeCompleted',
|
||||||
|
{
|
||||||
uuid: {
|
uuid: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
unique: true,
|
unique: true,
|
||||||
primaryKey: true
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
challenge: {
|
challenge: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
},
|
},
|
||||||
signed_event: {
|
signed_event: {
|
||||||
type: DataTypes.JSONB,
|
type: DataTypes.JSONB,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
},
|
},
|
||||||
public_key: {
|
public_key: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tableName: 'nostr_challenge_completed',
|
||||||
}
|
}
|
||||||
}, {
|
);
|
||||||
tableName: 'nostr_challenge_completed'
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = NostrChallengeCompleted;
|
module.exports = NostrChallengeCompleted;
|
||||||
|
|
@ -1,27 +1,31 @@
|
||||||
const { DataTypes } = require('sequelize');
|
const { DataTypes } = require('sequelize');
|
||||||
const sequelize = require('../database');
|
const sequelize = require('../database');
|
||||||
|
|
||||||
const NostrChallengeCreated = sequelize.define('NostrChallengeCreated', {
|
const NostrChallengeCreated = sequelize.define(
|
||||||
|
'NostrChallengeCreated',
|
||||||
|
{
|
||||||
uuid: {
|
uuid: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
unique: true,
|
unique: true,
|
||||||
primaryKey: true
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
challenge: {
|
challenge: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
},
|
},
|
||||||
expires_at: {
|
expires_at: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tableName: 'nostr_challenge_created',
|
||||||
}
|
}
|
||||||
}, {
|
);
|
||||||
tableName: 'nostr_challenge_created'
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = NostrChallengeCreated;
|
module.exports = NostrChallengeCreated;
|
||||||
|
|
@ -1,27 +1,31 @@
|
||||||
const { DataTypes } = require('sequelize');
|
const { DataTypes } = require('sequelize');
|
||||||
const sequelize = require('../database');
|
const sequelize = require('../database');
|
||||||
|
|
||||||
const NymSet = sequelize.define('NymSet', {
|
const NymSet = sequelize.define(
|
||||||
|
'NymSet',
|
||||||
|
{
|
||||||
uuid: {
|
uuid: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
unique: true,
|
unique: true,
|
||||||
primaryKey: true
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
public_key: {
|
public_key: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
},
|
},
|
||||||
nym: {
|
nym: {
|
||||||
type: DataTypes.TEXT,
|
type: DataTypes.TEXT,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tableName: 'nym_set',
|
||||||
}
|
}
|
||||||
}, {
|
);
|
||||||
tableName: 'nym_set'
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = NymSet;
|
module.exports = NymSet;
|
||||||
|
|
@ -1,23 +1,27 @@
|
||||||
const { DataTypes } = require('sequelize');
|
const { DataTypes } = require('sequelize');
|
||||||
const sequelize = require('../database');
|
const sequelize = require('../database');
|
||||||
|
|
||||||
const SessionCreated = sequelize.define('SessionCreated', {
|
const SessionCreated = sequelize.define(
|
||||||
|
'SessionCreated',
|
||||||
|
{
|
||||||
uuid: {
|
uuid: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
unique: true,
|
unique: true,
|
||||||
primaryKey: true
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
},
|
},
|
||||||
expires_at: {
|
expires_at: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tableName: 'session_created',
|
||||||
}
|
}
|
||||||
}, {
|
);
|
||||||
tableName: 'session_created'
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = SessionCreated;
|
module.exports = SessionCreated;
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
const { DataTypes } = require('sequelize');
|
const { DataTypes } = require('sequelize');
|
||||||
const sequelize = require('../database');
|
const sequelize = require('../database');
|
||||||
|
|
||||||
const SessionRelatedToPublickey = sequelize.define('SessionRelatedToPublickey', {
|
const SessionRelatedToPublickey = sequelize.define(
|
||||||
|
'SessionRelatedToPublickey',
|
||||||
|
{
|
||||||
uuid: {
|
uuid: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
unique: true,
|
unique: true,
|
||||||
primaryKey: true
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
session_uuid: {
|
session_uuid: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
|
|
@ -14,14 +16,16 @@ const SessionRelatedToPublickey = sequelize.define('SessionRelatedToPublickey',
|
||||||
},
|
},
|
||||||
public_key: {
|
public_key: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tableName: 'session_related_to_public_key',
|
||||||
}
|
}
|
||||||
}, {
|
);
|
||||||
tableName: 'session_related_to_public_key'
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = SessionRelatedToPublickey;
|
module.exports = SessionRelatedToPublickey;
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
const { DataTypes } = require('sequelize');
|
const { DataTypes } = require('sequelize');
|
||||||
const sequelize = require('../database');
|
const sequelize = require('../database');
|
||||||
|
|
||||||
const SignUpChallengeCompleted = sequelize.define('SignUpChallengeCompleted', {
|
const SignUpChallengeCompleted = sequelize.define(
|
||||||
|
'SignUpChallengeCompleted',
|
||||||
|
{
|
||||||
uuid: {
|
uuid: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
unique: true,
|
unique: true,
|
||||||
primaryKey: true
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
nostr_challenge_completed_uuid: {
|
nostr_challenge_completed_uuid: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
|
|
@ -14,18 +16,20 @@ const SignUpChallengeCompleted = sequelize.define('SignUpChallengeCompleted', {
|
||||||
},
|
},
|
||||||
app_invite_uuid: {
|
app_invite_uuid: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
},
|
},
|
||||||
public_key: {
|
public_key: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tableName: 'sign_up_challenge_completed',
|
||||||
}
|
}
|
||||||
}, {
|
);
|
||||||
tableName: 'sign_up_challenge_completed'
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = SignUpChallengeCompleted;
|
module.exports = SignUpChallengeCompleted;
|
||||||
|
|
@ -1,27 +1,31 @@
|
||||||
const { DataTypes } = require('sequelize');
|
const { DataTypes } = require('sequelize');
|
||||||
const sequelize = require('../database');
|
const sequelize = require('../database');
|
||||||
|
|
||||||
const SignUpChallengeCreated = sequelize.define('SignUpChallengeCreated', {
|
const SignUpChallengeCreated = sequelize.define(
|
||||||
|
'SignUpChallengeCreated',
|
||||||
|
{
|
||||||
uuid: {
|
uuid: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
unique: true,
|
unique: true,
|
||||||
primaryKey: true
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
nostr_challenge_uuid: {
|
nostr_challenge_uuid: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
},
|
},
|
||||||
app_invite_uuid: {
|
app_invite_uuid: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
},
|
},
|
||||||
created_at: {
|
created_at: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
allowNull: false
|
allowNull: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tableName: 'sign_up_challenge_created',
|
||||||
}
|
}
|
||||||
}, {
|
);
|
||||||
tableName: 'sign_up_challenge_created'
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = SignUpChallengeCreated;
|
module.exports = SignUpChallengeCreated;
|
||||||
|
|
@ -9,7 +9,9 @@ class ContactDetails {
|
||||||
}
|
}
|
||||||
|
|
||||||
removeDetails(type, value) {
|
removeDetails(type, value) {
|
||||||
this.details = this.details.filter(detail => detail.type !== type || detail.value !== value);
|
this.details = this.details.filter(
|
||||||
|
(detail) => detail.type !== type || detail.value !== value
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
syncUi() {
|
syncUi() {
|
||||||
|
|
@ -19,20 +21,20 @@ class ContactDetails {
|
||||||
const addedDetailFragment = this.buildContactDetailBadge(detail);
|
const addedDetailFragment = this.buildContactDetailBadge(detail);
|
||||||
this.rootUiElement.appendChild(addedDetailFragment);
|
this.rootUiElement.appendChild(addedDetailFragment);
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
buildContactDetailBadge(detail) {
|
buildContactDetailBadge(detail) {
|
||||||
const fragment = document.createDocumentFragment();
|
const fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
const div = document.createElement("div");
|
const div = document.createElement('div');
|
||||||
div.className = "added-contact-detail badge";
|
div.className = 'added-contact-detail badge';
|
||||||
|
|
||||||
const p = document.createElement("p");
|
const p = document.createElement('p');
|
||||||
p.textContent = `${detail.type}: ${detail.value}`;
|
p.textContent = `${detail.type}: ${detail.value}`;
|
||||||
|
|
||||||
const button = document.createElement("button");
|
const button = document.createElement('button');
|
||||||
button.textContent = "Eliminar";
|
button.textContent = 'Eliminar';
|
||||||
button.onclick = () => {
|
button.onclick = () => {
|
||||||
this.removeDetails(detail.type, detail.value);
|
this.removeDetails(detail.type, detail.value);
|
||||||
this.syncUi();
|
this.syncUi();
|
||||||
|
|
@ -48,7 +50,10 @@ class ContactDetails {
|
||||||
|
|
||||||
async getEncryptedContactDetails() {
|
async getEncryptedContactDetails() {
|
||||||
const jsonString = JSON.stringify(this.details);
|
const jsonString = JSON.stringify(this.details);
|
||||||
const encryptedContactDetails = await window.nostr.nip04.encrypt(await window.nostr.getPublicKey(), jsonString);
|
const encryptedContactDetails = await window.nostr.nip04.encrypt(
|
||||||
|
await window.nostr.getPublicKey(),
|
||||||
|
jsonString
|
||||||
|
);
|
||||||
return encryptedContactDetails;
|
return encryptedContactDetails;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -56,9 +61,11 @@ class ContactDetails {
|
||||||
let contactDetails;
|
let contactDetails;
|
||||||
|
|
||||||
window.onload = () => {
|
window.onload = () => {
|
||||||
contactDetails = new ContactDetails(document.querySelector('#created-contact-details-list'));
|
contactDetails = new ContactDetails(
|
||||||
|
document.querySelector('#created-contact-details-list')
|
||||||
|
);
|
||||||
|
|
||||||
document.querySelectorAll('.contact-detail-add-button').forEach(button => {
|
document.querySelectorAll('.contact-detail-add-button').forEach((button) => {
|
||||||
button.addEventListener('click', function () {
|
button.addEventListener('click', function () {
|
||||||
const badge = this.parentElement;
|
const badge = this.parentElement;
|
||||||
const type = badge.getAttribute('data-type');
|
const type = badge.getAttribute('data-type');
|
||||||
|
|
@ -76,29 +83,24 @@ window.onload = () => {
|
||||||
|
|
||||||
document
|
document
|
||||||
.querySelector('#submit-details-button')
|
.querySelector('#submit-details-button')
|
||||||
.addEventListener(
|
.addEventListener('click', async () => {
|
||||||
'click',
|
const encryptedContactDetails =
|
||||||
async () => {
|
await contactDetails.getEncryptedContactDetails();
|
||||||
const encryptedContactDetails = await contactDetails.getEncryptedContactDetails();
|
await fetch('/api/set-contact-details', {
|
||||||
await fetch('/api/set-contact-details',
|
|
||||||
{
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ encryptedContactDetails })
|
body: JSON.stringify({ encryptedContactDetails }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const nym = document.querySelector('#nym-input').value;
|
const nym = document.querySelector('#nym-input').value;
|
||||||
await fetch('/api/set-nym',
|
await fetch('/api/set-nym', {
|
||||||
{
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ nym })
|
body: JSON.stringify({ nym }),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
@ -1,22 +1,21 @@
|
||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
if (!window.nostr) {
|
if (!window.nostr) {
|
||||||
console.log("Nostr extension not present");
|
console.log('Nostr extension not present');
|
||||||
document.querySelector('#nostr-signup').disabled = true;
|
document.querySelector('#nostr-signup').disabled = true;
|
||||||
document.querySelector('#no-extension-nudges').style.display = 'block';
|
document.querySelector('#no-extension-nudges').style.display = 'block';
|
||||||
} else {
|
} else {
|
||||||
console.log("Nostr extension present");
|
console.log('Nostr extension present');
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
async function acceptInvite() {
|
async function acceptInvite() {
|
||||||
|
|
||||||
let challengeResponse;
|
let challengeResponse;
|
||||||
try {
|
try {
|
||||||
challengeResponse = await fetch('/api/signup/nostr-challenge', {
|
challengeResponse = await fetch('/api/signup/nostr-challenge', {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(`Something went wrong: ${error}`);
|
console.log(`Something went wrong: ${error}`);
|
||||||
|
|
@ -35,9 +34,9 @@ async function acceptInvite() {
|
||||||
const event = {
|
const event = {
|
||||||
kind: 22242,
|
kind: 22242,
|
||||||
created_at: Math.floor(Date.now() / 1000),
|
created_at: Math.floor(Date.now() / 1000),
|
||||||
tags: [["challenge", challenge]],
|
tags: [['challenge', challenge]],
|
||||||
content: "Sign this challenge to authenticate",
|
content: 'Sign this challenge to authenticate',
|
||||||
pubkey: pubkey
|
pubkey: pubkey,
|
||||||
};
|
};
|
||||||
|
|
||||||
let signedEvent;
|
let signedEvent;
|
||||||
|
|
@ -50,9 +49,9 @@ async function acceptInvite() {
|
||||||
|
|
||||||
let verifyResponse;
|
let verifyResponse;
|
||||||
try {
|
try {
|
||||||
verifyResponse = await fetch("/api/signup/nostr-verify", {
|
verifyResponse = await fetch('/api/signup/nostr-verify', {
|
||||||
method: "POST",
|
method: 'POST',
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(signedEvent),
|
body: JSON.stringify(signedEvent),
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -63,7 +62,7 @@ async function acceptInvite() {
|
||||||
if (verifyResponse.ok) {
|
if (verifyResponse.ok) {
|
||||||
document.querySelector('#sign-up-success').style.display = 'block';
|
document.querySelector('#sign-up-success').style.display = 'block';
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = "/createProfile";
|
window.location.href = '/createProfile';
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -15,84 +15,82 @@ router.get('/signup/nostr-challenge', async (req, res) => {
|
||||||
|
|
||||||
let signUpChallenge;
|
let signUpChallenge;
|
||||||
try {
|
try {
|
||||||
signUpChallenge = await invitesService.createSignUpChallenge(
|
signUpChallenge = await invitesService.createSignUpChallenge(inviteUuid);
|
||||||
inviteUuid
|
|
||||||
)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof errors.NotFoundError) {
|
if (error instanceof errors.NotFoundError) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Could not find invite with that id.'
|
message: 'Could not find invite with that id.',
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error instanceof errors.AlreadyUsedError) {
|
if (error instanceof errors.AlreadyUsedError) {
|
||||||
return res.status(410).json({
|
return res.status(410).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'That invite has already been used.'
|
message: 'That invite has already been used.',
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Unexpected error.'
|
message: 'Unexpected error.',
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let relatedNostrChallenge;
|
let relatedNostrChallenge;
|
||||||
try {
|
try {
|
||||||
relatedNostrChallenge = await nostrService.getNostrChallenge(
|
relatedNostrChallenge = await nostrService.getNostrChallenge(
|
||||||
signUpChallenge.nostr_challenge_uuid
|
signUpChallenge.nostr_challenge_uuid
|
||||||
)
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Unexpected error.'
|
message: 'Unexpected error.',
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).json({ 'challenge': relatedNostrChallenge.challenge });
|
return res.status(200).json({ challenge: relatedNostrChallenge.challenge });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.post('/signup/nostr-verify', async (req, res) => {
|
||||||
router.post("/signup/nostr-verify", async (req, res) => {
|
|
||||||
const signedEvent = req.body;
|
const signedEvent = req.body;
|
||||||
const sessionUuid = req.cookies.sessionUuid;
|
const sessionUuid = req.cookies.sessionUuid;
|
||||||
|
|
||||||
let completedSignUpChallenge;
|
let completedSignUpChallenge;
|
||||||
try {
|
try {
|
||||||
completedSignUpChallenge = await invitesService.verifySignUpChallenge(signedEvent);
|
completedSignUpChallenge =
|
||||||
|
await invitesService.verifySignUpChallenge(signedEvent);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof errors.ExpiredError) {
|
if (error instanceof errors.ExpiredError) {
|
||||||
return res.status(410).json({
|
return res.status(410).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'The challenge has expired, request a new one.'
|
message: 'The challenge has expired, request a new one.',
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
if (error instanceof errors.AlreadyUsedError) {
|
if (error instanceof errors.AlreadyUsedError) {
|
||||||
return res.status(410).json({
|
return res.status(410).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'The challenge has been used, request a new one.'
|
message: 'The challenge has been used, request a new one.',
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
if (error instanceof errors.InvalidSignatureError) {
|
if (error instanceof errors.InvalidSignatureError) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'The challenge signature is not valid.'
|
message: 'The challenge signature is not valid.',
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await sessionService.relateSessionToPublicKey(
|
await sessionService.relateSessionToPublicKey(
|
||||||
sessionUuid,
|
sessionUuid,
|
||||||
completedSignUpChallenge.public_key
|
completedSignUpChallenge.public_key
|
||||||
)
|
);
|
||||||
|
|
||||||
return res.status(200).json({ success: true });
|
return res.status(200).json({ success: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/set-contact-details",
|
'/set-contact-details',
|
||||||
rejectIfNotAuthorizedMiddleware,
|
rejectIfNotAuthorizedMiddleware,
|
||||||
attachPublicKeyMiddleware,
|
attachPublicKeyMiddleware,
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
|
|
@ -102,24 +100,21 @@ router.post(
|
||||||
if (!encryptedContactDetails) {
|
if (!encryptedContactDetails) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Missing contact details.'
|
message: 'Missing contact details.',
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await profileService.setContactDetails(
|
await profileService.setContactDetails(publicKey, encryptedContactDetails);
|
||||||
publicKey,
|
|
||||||
encryptedContactDetails
|
|
||||||
)
|
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
message: 'Contact details set successfully.'
|
message: 'Contact details set successfully.',
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/set-nym",
|
'/set-nym',
|
||||||
rejectIfNotAuthorizedMiddleware,
|
rejectIfNotAuthorizedMiddleware,
|
||||||
attachPublicKeyMiddleware,
|
attachPublicKeyMiddleware,
|
||||||
async (req, res) => {
|
async (req, res) => {
|
||||||
|
|
@ -129,19 +124,16 @@ router.post(
|
||||||
if (!nym) {
|
if (!nym) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Missing nym'
|
message: 'Missing nym',
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await profileService.setNym(
|
await profileService.setNym(publicKey, nym);
|
||||||
publicKey,
|
|
||||||
nym
|
|
||||||
)
|
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
message: 'Nym set successfully.'
|
message: 'Nym set successfully.',
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
const redirectIfNotAuthorizedMiddleware = require('../middlewares/redirectIfNotAuthorizedMiddleware');
|
const redirectIfNotAuthorizedMiddleware = require('../middlewares/redirectIfNotAuthorizedMiddleware');
|
||||||
const invitesService = require('../services/invitesService')
|
const invitesService = require('../services/invitesService');
|
||||||
|
|
||||||
router.get('/', (req, res) => {
|
router.get('/', (req, res) => {
|
||||||
res.render('index', { uuid: req.cookies.sessionUuid });
|
res.render('index', { uuid: req.cookies.sessionUuid });
|
||||||
|
|
@ -21,9 +21,8 @@ router.get('/invite/:inviteUuid', async (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await invitesService.isAppInviteSpent(inviteUuid)) {
|
if (await invitesService.isAppInviteSpent(inviteUuid)) {
|
||||||
return res.status(410).render('invite_spent', { invite })
|
return res.status(410).render('invite_spent', { invite });
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching invite:', error);
|
console.error('Error fetching invite:', error);
|
||||||
return res.status(500).render('error', { message: 'An error occurred' });
|
return res.status(500).render('error', { message: 'An error occurred' });
|
||||||
|
|
@ -45,9 +44,8 @@ router.get('/invite/:inviteUuid', async (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await invitesService.isAppInviteSpent(inviteUuid)) {
|
if (await invitesService.isAppInviteSpent(inviteUuid)) {
|
||||||
return res.status(410).render('invite_spent', { invite })
|
return res.status(410).render('invite_spent', { invite });
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching invite:', error);
|
console.error('Error fetching invite:', error);
|
||||||
return res.status(500).render('error', { message: 'An error occurred' });
|
return res.status(500).render('error', { message: 'An error occurred' });
|
||||||
|
|
@ -56,9 +54,13 @@ router.get('/invite/:inviteUuid', async (req, res) => {
|
||||||
return res.render('invite', { invite });
|
return res.render('invite', { invite });
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/createProfile', redirectIfNotAuthorizedMiddleware, async (req, res) => {
|
router.get(
|
||||||
|
'/createProfile',
|
||||||
|
redirectIfNotAuthorizedMiddleware,
|
||||||
|
async (req, res) => {
|
||||||
return res.status(200).render('createProfile');
|
return res.status(200).render('createProfile');
|
||||||
})
|
}
|
||||||
|
);
|
||||||
|
|
||||||
router.get('/private', redirectIfNotAuthorizedMiddleware, (req, res) => {
|
router.get('/private', redirectIfNotAuthorizedMiddleware, (req, res) => {
|
||||||
res.render('private', {});
|
res.render('private', {});
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@ const SignUpChallengeCompleted = require('../models/SignUpChallengeCompleted');
|
||||||
const errors = require('../errors');
|
const errors = require('../errors');
|
||||||
|
|
||||||
async function appInviteExists(inviteUuid) {
|
async function appInviteExists(inviteUuid) {
|
||||||
const invite = await AppInviteCreated.findOne({ where: { uuid: inviteUuid } });
|
const invite = await AppInviteCreated.findOne({
|
||||||
|
where: { uuid: inviteUuid },
|
||||||
|
});
|
||||||
if (invite) {
|
if (invite) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -16,16 +18,18 @@ async function appInviteExists(inviteUuid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAppInvite(inviteUuid) {
|
async function getAppInvite(inviteUuid) {
|
||||||
const invite = await AppInviteCreated.findOne({ where: { uuid: inviteUuid } });
|
const invite = await AppInviteCreated.findOne({
|
||||||
|
where: { uuid: inviteUuid },
|
||||||
|
});
|
||||||
return invite;
|
return invite;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function isAppInviteSpent(appInviteUuid) {
|
async function isAppInviteSpent(appInviteUuid) {
|
||||||
const signUpChallengeCompleted = await SignUpChallengeCompleted.findOne({
|
const signUpChallengeCompleted = await SignUpChallengeCompleted.findOne({
|
||||||
where: {
|
where: {
|
||||||
app_invite_uuid: appInviteUuid
|
app_invite_uuid: appInviteUuid,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
if (signUpChallengeCompleted) {
|
if (signUpChallengeCompleted) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -37,82 +41,71 @@ async function createAppInvite(inviterPubKey) {
|
||||||
return await AppInviteCreated.create({
|
return await AppInviteCreated.create({
|
||||||
uuid: uuid.v7(),
|
uuid: uuid.v7(),
|
||||||
inviter_pub_key: inviterPubKey,
|
inviter_pub_key: inviterPubKey,
|
||||||
created_at: new Date().toISOString()
|
created_at: new Date().toISOString(),
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createSignUpChallenge(appInviteUuid) {
|
async function createSignUpChallenge(appInviteUuid) {
|
||||||
|
|
||||||
if (!(await appInviteExists(appInviteUuid))) {
|
if (!(await appInviteExists(appInviteUuid))) {
|
||||||
throw new errors.NotFoundError("Invite doesn't exist.")
|
throw new errors.NotFoundError("Invite doesn't exist.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await isAppInviteSpent(appInviteUuid)) {
|
if (await isAppInviteSpent(appInviteUuid)) {
|
||||||
throw new errors.AlreadyUsedError("Invite has already been used.")
|
throw new errors.AlreadyUsedError('Invite has already been used.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const nostrChallenge = await nostrService.createNostrChallenge()
|
const nostrChallenge = await nostrService.createNostrChallenge();
|
||||||
|
|
||||||
return await SignUpChallengeCreated.create({
|
return await SignUpChallengeCreated.create({
|
||||||
'uuid': uuid.v7(),
|
uuid: uuid.v7(),
|
||||||
nostr_challenge_uuid: nostrChallenge.uuid,
|
nostr_challenge_uuid: nostrChallenge.uuid,
|
||||||
app_invite_uuid: appInviteUuid,
|
app_invite_uuid: appInviteUuid,
|
||||||
created_at: new Date().toISOString()
|
created_at: new Date().toISOString(),
|
||||||
}
|
});
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function verifySignUpChallenge(signedEvent) {
|
async function verifySignUpChallenge(signedEvent) {
|
||||||
|
const challengeTag = signedEvent.tags.find((tag) => tag[0] === 'challenge');
|
||||||
const challengeTag = signedEvent.tags.find(tag => tag[0] === "challenge");
|
|
||||||
const challenge = challengeTag[1];
|
const challenge = challengeTag[1];
|
||||||
|
|
||||||
const nostrChallenge = await nostrService.getNostrChallenge(
|
const nostrChallenge = await nostrService.getNostrChallenge(null, challenge);
|
||||||
null, challenge
|
|
||||||
);
|
|
||||||
|
|
||||||
const signUpChallenge = await SignUpChallengeCreated.findOne({
|
const signUpChallenge = await SignUpChallengeCreated.findOne({
|
||||||
where: {
|
where: {
|
||||||
nostr_challenge_uuid: nostrChallenge.uuid
|
nostr_challenge_uuid: nostrChallenge.uuid,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
if (await nostrService.hasNostrChallengeBeenCompleted(challenge)) {
|
if (await nostrService.hasNostrChallengeBeenCompleted(challenge)) {
|
||||||
throw new errors.AlreadyUsedError("This challenge has already been used.");
|
throw new errors.AlreadyUsedError('This challenge has already been used.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const completedNostrChallenge = await nostrService.verifyNostrChallenge(signedEvent);
|
const completedNostrChallenge =
|
||||||
|
await nostrService.verifyNostrChallenge(signedEvent);
|
||||||
|
|
||||||
const completedSignUpChallenge = await SignUpChallengeCompleted.create(
|
const completedSignUpChallenge = await SignUpChallengeCompleted.create({
|
||||||
{
|
uuid: uuid.v7(),
|
||||||
'uuid': uuid.v7(),
|
|
||||||
nostr_challenge_completed_uuid: completedNostrChallenge.uuid,
|
nostr_challenge_completed_uuid: completedNostrChallenge.uuid,
|
||||||
app_invite_uuid: signUpChallenge.app_invite_uuid,
|
app_invite_uuid: signUpChallenge.app_invite_uuid,
|
||||||
public_key: completedNostrChallenge.public_key,
|
public_key: completedNostrChallenge.public_key,
|
||||||
created_at: new Date().toISOString()
|
created_at: new Date().toISOString(),
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
return completedSignUpChallenge;
|
return completedSignUpChallenge;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function isPublicKeySignedUp(publicKey) {
|
async function isPublicKeySignedUp(publicKey) {
|
||||||
const signUpChallengeCompleted = await SignUpChallengeCompleted.findOne(
|
const signUpChallengeCompleted = await SignUpChallengeCompleted.findOne({
|
||||||
{
|
|
||||||
where: {
|
where: {
|
||||||
public_key: publicKey
|
public_key: publicKey,
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
)
|
|
||||||
|
|
||||||
if (signUpChallengeCompleted) {
|
if (signUpChallengeCompleted) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
@ -122,5 +115,5 @@ module.exports = {
|
||||||
createAppInvite,
|
createAppInvite,
|
||||||
createSignUpChallenge,
|
createSignUpChallenge,
|
||||||
verifySignUpChallenge,
|
verifySignUpChallenge,
|
||||||
isPublicKeySignedUp
|
isPublicKeySignedUp,
|
||||||
};
|
};
|
||||||
|
|
@ -1,77 +1,78 @@
|
||||||
const uuid = require("uuid");
|
const uuid = require('uuid');
|
||||||
const crypto = require("crypto");
|
const crypto = require('crypto');
|
||||||
const { Op, TimeoutError } = require('sequelize');
|
const { Op, TimeoutError } = require('sequelize');
|
||||||
const { verifyEvent } = require("nostr-tools");
|
const { verifyEvent } = require('nostr-tools');
|
||||||
|
|
||||||
const NostrChallengeCreated = require('../models/NostrChallengeCreated');
|
const NostrChallengeCreated = require('../models/NostrChallengeCreated');
|
||||||
const NostrChallengeCompleted = require("../models/NostrChallengeCompleted");
|
const NostrChallengeCompleted = require('../models/NostrChallengeCompleted');
|
||||||
|
|
||||||
const constants = require('../constants');
|
const constants = require('../constants');
|
||||||
const errors = require('../errors');
|
const errors = require('../errors');
|
||||||
|
|
||||||
async function createNostrChallenge() {
|
async function createNostrChallenge() {
|
||||||
|
|
||||||
const currentTimestamp = new Date();
|
const currentTimestamp = new Date();
|
||||||
const expiryTimestamp = new Date(currentTimestamp.getTime());
|
const expiryTimestamp = new Date(currentTimestamp.getTime());
|
||||||
expiryTimestamp.setSeconds(expiryTimestamp.getSeconds() + constants.DEFAULT_NOSTR_CHALLENGE_DURATION_SECONDS);
|
expiryTimestamp.setSeconds(
|
||||||
|
expiryTimestamp.getSeconds() +
|
||||||
|
constants.DEFAULT_NOSTR_CHALLENGE_DURATION_SECONDS
|
||||||
|
);
|
||||||
|
|
||||||
const nostrChallenge = await NostrChallengeCreated.create({
|
const nostrChallenge = await NostrChallengeCreated.create({
|
||||||
'uuid': uuid.v7(),
|
uuid: uuid.v7(),
|
||||||
challenge: crypto.randomBytes(32).toString("hex"),
|
challenge: crypto.randomBytes(32).toString('hex'),
|
||||||
expires_at: expiryTimestamp.toISOString(),
|
expires_at: expiryTimestamp.toISOString(),
|
||||||
created_at: currentTimestamp.toISOString()
|
created_at: currentTimestamp.toISOString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return nostrChallenge;
|
return nostrChallenge;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getNostrChallenge(nostrChallengeUuid = null, challenge = null) {
|
async function getNostrChallenge(nostrChallengeUuid = null, challenge = null) {
|
||||||
|
|
||||||
if (nostrChallengeUuid) {
|
if (nostrChallengeUuid) {
|
||||||
return await NostrChallengeCreated.findOne({
|
return await NostrChallengeCreated.findOne({
|
||||||
where: {
|
where: {
|
||||||
'uuid': nostrChallengeUuid
|
uuid: nostrChallengeUuid,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (challenge) {
|
if (challenge) {
|
||||||
return await NostrChallengeCreated.findOne({
|
return await NostrChallengeCreated.findOne({
|
||||||
where: {
|
where: {
|
||||||
challenge
|
challenge,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
throw Error('You need to pass a uuid or a challenge.')
|
throw Error('You need to pass a uuid or a challenge.');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function verifyNostrChallenge(signedEvent) {
|
async function verifyNostrChallenge(signedEvent) {
|
||||||
const challengeTag = signedEvent.tags.find(tag => tag[0] === "challenge");
|
const challengeTag = signedEvent.tags.find((tag) => tag[0] === 'challenge');
|
||||||
const challenge = challengeTag[1];
|
const challenge = challengeTag[1];
|
||||||
|
|
||||||
if (!(await isNostrChallengeFresh(challenge))) {
|
if (!(await isNostrChallengeFresh(challenge))) {
|
||||||
throw TimeoutError("Challenge expired, request new one.");
|
throw TimeoutError('Challenge expired, request new one.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await hasNostrChallengeBeenCompleted(challenge)) {
|
if (await hasNostrChallengeBeenCompleted(challenge)) {
|
||||||
throw new errors.AlreadyUsedError("Challenge already used, request new one.");
|
throw new errors.AlreadyUsedError(
|
||||||
|
'Challenge already used, request new one.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSignatureValid = verifyEvent(signedEvent);
|
const isSignatureValid = verifyEvent(signedEvent);
|
||||||
if (!isSignatureValid) {
|
if (!isSignatureValid) {
|
||||||
throw new errors.InvalidSignatureError("Signature is not valid.");
|
throw new errors.InvalidSignatureError('Signature is not valid.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return await NostrChallengeCompleted.create({
|
return await NostrChallengeCompleted.create({
|
||||||
'uuid': uuid.v7(),
|
uuid: uuid.v7(),
|
||||||
challenge: challenge,
|
challenge: challenge,
|
||||||
signed_event: signedEvent,
|
signed_event: signedEvent,
|
||||||
public_key: signedEvent.pubkey,
|
public_key: signedEvent.pubkey,
|
||||||
created_at: new Date().toISOString()
|
created_at: new Date().toISOString(),
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function isNostrChallengeFresh(challengeString) {
|
async function isNostrChallengeFresh(challengeString) {
|
||||||
|
|
@ -79,9 +80,9 @@ async function isNostrChallengeFresh(challengeString) {
|
||||||
where: {
|
where: {
|
||||||
challenge: challengeString,
|
challenge: challengeString,
|
||||||
expires_at: {
|
expires_at: {
|
||||||
[Op.gt]: new Date()
|
[Op.gt]: new Date(),
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (nostrChallenge) {
|
if (nostrChallenge) {
|
||||||
|
|
@ -91,13 +92,11 @@ async function isNostrChallengeFresh(challengeString) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function hasNostrChallengeBeenCompleted(challengeString) {
|
async function hasNostrChallengeBeenCompleted(challengeString) {
|
||||||
const completedNostrChallenge = await NostrChallengeCompleted.findOne(
|
const completedNostrChallenge = await NostrChallengeCompleted.findOne({
|
||||||
{
|
|
||||||
where: {
|
where: {
|
||||||
challenge: challengeString
|
challenge: challengeString,
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
if (completedNostrChallenge) {
|
if (completedNostrChallenge) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -105,11 +104,10 @@ async function hasNostrChallengeBeenCompleted(challengeString) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
createNostrChallenge,
|
createNostrChallenge,
|
||||||
getNostrChallenge,
|
getNostrChallenge,
|
||||||
verifyNostrChallenge,
|
verifyNostrChallenge,
|
||||||
isNostrChallengeFresh,
|
isNostrChallengeFresh,
|
||||||
hasNostrChallengeBeenCompleted
|
hasNostrChallengeBeenCompleted,
|
||||||
};
|
};
|
||||||
|
|
@ -3,28 +3,24 @@ const ContactDetailsSet = require('../models/ContactDetailsSet');
|
||||||
const NymSet = require('../models/NymSet');
|
const NymSet = require('../models/NymSet');
|
||||||
|
|
||||||
async function setContactDetails(publicKey, encryptedContactDetails) {
|
async function setContactDetails(publicKey, encryptedContactDetails) {
|
||||||
return await ContactDetailsSet.create(
|
return await ContactDetailsSet.create({
|
||||||
{
|
uuid: uuid.v7(),
|
||||||
'uuid': uuid.v7(),
|
|
||||||
public_key: publicKey,
|
public_key: publicKey,
|
||||||
encrypted_contact_details: encryptedContactDetails,
|
encrypted_contact_details: encryptedContactDetails,
|
||||||
created_at: new Date().toISOString()
|
created_at: new Date().toISOString(),
|
||||||
}
|
});
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setNym(publicKey, nym) {
|
async function setNym(publicKey, nym) {
|
||||||
return await NymSet.create(
|
return await NymSet.create({
|
||||||
{
|
uuid: uuid.v7(),
|
||||||
'uuid': uuid.v7(),
|
|
||||||
public_key: publicKey,
|
public_key: publicKey,
|
||||||
nym: nym,
|
nym: nym,
|
||||||
created_at: new Date().toISOString()
|
created_at: new Date().toISOString(),
|
||||||
}
|
});
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
setContactDetails,
|
setContactDetails,
|
||||||
setNym
|
setNym,
|
||||||
};
|
};
|
||||||
|
|
@ -9,20 +9,22 @@ const constants = require('../constants');
|
||||||
async function createSession(sessionUuid) {
|
async function createSession(sessionUuid) {
|
||||||
const currentTimestamp = new Date();
|
const currentTimestamp = new Date();
|
||||||
const expiryTimestamp = new Date(currentTimestamp.getTime());
|
const expiryTimestamp = new Date(currentTimestamp.getTime());
|
||||||
expiryTimestamp.setSeconds(expiryTimestamp.getSeconds() + constants.DEFAULT_SESSION_DURATION_SECONDS);
|
expiryTimestamp.setSeconds(
|
||||||
|
expiryTimestamp.getSeconds() + constants.DEFAULT_SESSION_DURATION_SECONDS
|
||||||
|
);
|
||||||
|
|
||||||
return await SessionCreated.create({
|
return await SessionCreated.create({
|
||||||
uuid: sessionUuid,
|
uuid: sessionUuid,
|
||||||
created_at: currentTimestamp.toISOString(),
|
created_at: currentTimestamp.toISOString(),
|
||||||
expires_at: expiryTimestamp.toISOString()
|
expires_at: expiryTimestamp.toISOString(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function isSessionValid(sessionUuid) {
|
async function isSessionValid(sessionUuid) {
|
||||||
const currentSession = await SessionCreated.findOne({
|
const currentSession = await SessionCreated.findOne({
|
||||||
where: {
|
where: {
|
||||||
'uuid': sessionUuid
|
uuid: sessionUuid,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!currentSession) {
|
if (!currentSession) {
|
||||||
|
|
@ -38,30 +40,27 @@ async function isSessionValid(sessionUuid) {
|
||||||
|
|
||||||
async function relateSessionToPublicKey(sessionUuid, publicKey) {
|
async function relateSessionToPublicKey(sessionUuid, publicKey) {
|
||||||
if (!(await isSessionValid(sessionUuid))) {
|
if (!(await isSessionValid(sessionUuid))) {
|
||||||
throw Error("Session is not valid anymore.");
|
throw Error('Session is not valid anymore.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(await invitesService.isPublicKeySignedUp(publicKey))) {
|
if (!(await invitesService.isPublicKeySignedUp(publicKey))) {
|
||||||
throw Error("Public key is not signed up.");
|
throw Error('Public key is not signed up.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return SessionRelatedToPublickey.create({
|
return SessionRelatedToPublickey.create({
|
||||||
'uuid': uuid.v7(),
|
uuid: uuid.v7(),
|
||||||
session_uuid: sessionUuid,
|
session_uuid: sessionUuid,
|
||||||
public_key: publicKey,
|
public_key: publicKey,
|
||||||
created_at: new Date().toISOString()
|
created_at: new Date().toISOString(),
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function isSessionAuthorized(sessionUuid) {
|
async function isSessionAuthorized(sessionUuid) {
|
||||||
const isSessionRelatedToPublicKey = await SessionRelatedToPublickey.findOne(
|
const isSessionRelatedToPublicKey = await SessionRelatedToPublickey.findOne({
|
||||||
{
|
|
||||||
where: {
|
where: {
|
||||||
session_uuid: sessionUuid
|
session_uuid: sessionUuid,
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
if (isSessionRelatedToPublicKey) {
|
if (isSessionRelatedToPublicKey) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -71,16 +70,13 @@ async function isSessionAuthorized(sessionUuid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getPublicKeyRelatedToSession(sessionUuid) {
|
async function getPublicKeyRelatedToSession(sessionUuid) {
|
||||||
const sessionRelatedToPublickey = await SessionRelatedToPublickey.findOne(
|
const sessionRelatedToPublickey = await SessionRelatedToPublickey.findOne({
|
||||||
{
|
|
||||||
where: {
|
where: {
|
||||||
session_uuid: sessionUuid
|
session_uuid: sessionUuid,
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
return sessionRelatedToPublickey.public_key;
|
return sessionRelatedToPublickey.public_key;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
@ -88,5 +84,5 @@ module.exports = {
|
||||||
isSessionValid,
|
isSessionValid,
|
||||||
relateSessionToPublicKey,
|
relateSessionToPublicKey,
|
||||||
isSessionAuthorized,
|
isSessionAuthorized,
|
||||||
getPublicKeyRelatedToSession
|
getPublicKeyRelatedToSession,
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,53 +1,77 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
<head>
|
||||||
<head>
|
|
||||||
<title>Crear perfil</title>
|
<title>Crear perfil</title>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link rel="stylesheet" href="/css/seca.css">
|
<link rel="stylesheet" href="/css/seca.css" />
|
||||||
<script src="/javascript/createProfile.js"></script>
|
<script src="/javascript/createProfile.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1>Crea tu perfil</h1>
|
<h1>Crea tu perfil</h1>
|
||||||
<p>Tu clave de Nostr ya es parte de la seca.</p>
|
<p>Tu clave de Nostr ya es parte de la seca.</p>
|
||||||
<p>Añade detalles a tu perfil para poder empezar a comerciar.</p>
|
<p>Añade detalles a tu perfil para poder empezar a comerciar.</p>
|
||||||
</p>
|
<hr />
|
||||||
<hr>
|
|
||||||
<form onsubmit="return false">
|
<form onsubmit="return false">
|
||||||
<label>Pseudónimo (Nym):<input type="text" name="nym" id="nym-input"></label>
|
<label
|
||||||
|
>Pseudónimo (Nym):<input type="text" name="nym" id="nym-input"
|
||||||
|
/></label>
|
||||||
<div id="contacts">
|
<div id="contacts">
|
||||||
<p>Añade métodos de contacto para poder hablar con otros miembros.</p>
|
<p>Añade métodos de contacto para poder hablar con otros miembros.</p>
|
||||||
<div class="badges">
|
<div class="badges">
|
||||||
<div class="badge" data-type="whatsapp">📱 Teléfono <input><button
|
<div class="badge" data-type="whatsapp">
|
||||||
class="contact-detail-add-button">Añadir</button></div>
|
📱 Teléfono <input /><button class="contact-detail-add-button">
|
||||||
<div class="badge" data-type="whatsapp">📱 WhatsApp <input><button
|
Añadir
|
||||||
class="contact-detail-add-button">Añadir</button></div>
|
</button>
|
||||||
<div class="badge" data-type="telegram">📩 Telegram <input><button
|
</div>
|
||||||
class="contact-detail-add-button"></button>>Añadir</button></div>
|
<div class="badge" data-type="whatsapp">
|
||||||
<div class="badge" data-type="email">📧 Email <input><button
|
📱 WhatsApp <input /><button class="contact-detail-add-button">
|
||||||
class="contact-detail-add-button">Añadir</button></div>
|
Añadir
|
||||||
<div class="badge" data-type="nostr">📧 Nostr <input><button
|
</button>
|
||||||
class="contact-detail-add-button">Añadir</button></div>
|
</div>
|
||||||
<div class="badge" data-type="signal">📧 Signal <input><button
|
<div class="badge" data-type="telegram">
|
||||||
class="contact-detail-add-button">Añadir</button></div>
|
📩 Telegram <input /><button class="contact-detail-add-button">
|
||||||
<div class="badge" data-type="matrix">📧 Matrix <input><button
|
Añadir
|
||||||
class="contact-detail-add-button">Añadir</button></div>
|
</button>
|
||||||
<div class="badge" data-type="xmpp">📧 XMPP <input><button
|
</div>
|
||||||
class="contact-detail-add-button">Añadir</button></div>
|
<div class="badge" data-type="email">
|
||||||
<div class="badge" data-type="simplex">📧 Simplex <input><button
|
📧 Email <input /><button class="contact-detail-add-button">
|
||||||
class="contact-detail-add-button">Añadir</button></div>
|
Añadir
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="badge" data-type="nostr">
|
||||||
|
📧 Nostr <input /><button class="contact-detail-add-button">
|
||||||
|
Añadir
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="badge" data-type="signal">
|
||||||
|
📧 Signal <input /><button class="contact-detail-add-button">
|
||||||
|
Añadir
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="badge" data-type="matrix">
|
||||||
|
📧 Matrix <input /><button class="contact-detail-add-button">
|
||||||
|
Añadir
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="badge" data-type="xmpp">
|
||||||
|
📧 XMPP <input /><button class="contact-detail-add-button">
|
||||||
|
Añadir
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="badge" data-type="simplex">
|
||||||
|
📧 Simplex <input /><button class="contact-detail-add-button">
|
||||||
|
Añadir
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="created-contact-details-section">
|
<div id="created-contact-details-section">
|
||||||
<p>Contactos añadidos</p>
|
<p>Contactos añadidos</p>
|
||||||
<div id="created-contact-details-list">
|
<div id="created-contact-details-list"></div>
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<button id="submit-details-button" type="submit">Crear tu perfil</button>
|
<button id="submit-details-button" type="submit">Crear tu perfil</button>
|
||||||
</form>
|
</form>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -1,17 +1,13 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
<head>
|
||||||
<head>
|
<meta charset="UTF-8" />
|
||||||
<meta charset="UTF-8">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Error</title>
|
<title>Error</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1>Error</h1>
|
<h1>Error</h1>
|
||||||
<p>
|
<p><%= message %></p>
|
||||||
<%= message %>
|
</body>
|
||||||
</p>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -1,23 +1,26 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
<head>
|
||||||
<head>
|
|
||||||
<title>Hello World</title>
|
<title>Hello World</title>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<script src="/javascript/index.js"></script>
|
<script src="/javascript/index.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1>Bienvenido a la seca</h1>
|
<h1>Bienvenido a la seca</h1>
|
||||||
<p>Usa Nostr para logearte</p>
|
<p>Usa Nostr para logearte</p>
|
||||||
<form onsubmit="login();return false">
|
<form onsubmit="login();return false">
|
||||||
<button type="submit">Login con extensión de Nostr</button>
|
<button type="submit">Login con extensión de Nostr</button>
|
||||||
</form>
|
</form>
|
||||||
<p>
|
<p>
|
||||||
¿No tienes cuenta de Nostr? <a href="https://start.njump.me/" target="_blank" rel="noopener noreferrer">Crea una
|
¿No tienes cuenta de Nostr?
|
||||||
gratis</a>.
|
<a
|
||||||
|
href="https://start.njump.me/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>Crea una gratis</a
|
||||||
|
>.
|
||||||
</p>
|
</p>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -1,53 +1,88 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
<head>
|
||||||
<head>
|
|
||||||
<title>Invite Details</title>
|
<title>Invite Details</title>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<script src="/javascript/invite.js"></script>
|
<script src="/javascript/invite.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1>¡Has sido invitado!</h1>
|
<h1>¡Has sido invitado!</h1>
|
||||||
<p>Has sido invitado a la seca.</p>
|
<p>Has sido invitado a la seca.</p>
|
||||||
<p>Invite UUID: <%= invite.uuid %>
|
<p>Invite UUID: <%= invite.uuid %></p>
|
||||||
</p>
|
|
||||||
<p>Usa tu extensión de Nostr para darte de alta:</p>
|
<p>Usa tu extensión de Nostr para darte de alta:</p>
|
||||||
<form onsubmit="acceptInvite();return false">
|
<form onsubmit="acceptInvite();return false">
|
||||||
<button id="nostr-signup" type="submit">Alta con Nostr</button>
|
<button id="nostr-signup" type="submit">Alta con Nostr</button>
|
||||||
</form>
|
</form>
|
||||||
<div id="rejected-nostr-nudges" style="display:none">
|
<div id="rejected-nostr-nudges" style="display: none">
|
||||||
<p>Ups, parece que no has aceptado que usemos tus claves. Si te has equivocado, puedes intentarlo de nuevo.</p>
|
<p>
|
||||||
|
Ups, parece que no has aceptado que usemos tus claves. Si te has
|
||||||
|
equivocado, puedes intentarlo de nuevo.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div id="no-extension-nudges" style="display:none">
|
<div id="no-extension-nudges" style="display: none">
|
||||||
<p>¡Atención! No se ha encontrado una extensión de Nostr en tu navegador. Puedes usar: </p>
|
<p>
|
||||||
|
¡Atención! No se ha encontrado una extensión de Nostr en tu navegador.
|
||||||
|
Puedes usar:
|
||||||
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Firefox
|
<li>
|
||||||
|
Firefox
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="https://addons.mozilla.org/en-US/firefox/addon/alby/" target="_blank"
|
<li>
|
||||||
rel="noopener noreferrer">Alby</a></li>
|
<a
|
||||||
<li><a href="https://addons.mozilla.org/en-US/firefox/addon/nos2x-fox/" target="_blank"
|
href="https://addons.mozilla.org/en-US/firefox/addon/alby/"
|
||||||
rel="noopener noreferrer">nos2x-fox</a></li>
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>Alby</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://addons.mozilla.org/en-US/firefox/addon/nos2x-fox/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>nos2x-fox</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Chrome
|
Chrome
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="https://chromewebstore.google.com/detail/alby-bitcoin-wallet-for-l/iokeahhehimjnekafflcihljlcjccdbe?pli=1"
|
<li>
|
||||||
target="_blank" rel="noopener noreferrer">Alby</a></li>
|
<a
|
||||||
<li><a href="https://chromewebstore.google.com/detail/nos2x/kpgefcfmnafjgpblomihpgmejjdanjjp"
|
href="https://chromewebstore.google.com/detail/alby-bitcoin-wallet-for-l/iokeahhehimjnekafflcihljlcjccdbe?pli=1"
|
||||||
target="_blank" rel="noopener noreferrer">nos2x</a></li>
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>Alby</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://chromewebstore.google.com/detail/nos2x/kpgefcfmnafjgpblomihpgmejjdanjjp"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>nos2x</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div id="sign-up-success" style="display:none">
|
<div id="sign-up-success" style="display: none">
|
||||||
<p>¡Bien! Hemos dado de alta tu clave de Nostr.</p>
|
<p>¡Bien! Hemos dado de alta tu clave de Nostr.</p>
|
||||||
<p>Te vamos a redirigir a la seca, espera un momento.</p>
|
<p>Te vamos a redirigir a la seca, espera un momento.</p>
|
||||||
</div>
|
</div>
|
||||||
<p>¿No tienes cuenta de Nostr? <a href="https://start.njump.me/" target="_blank" rel="noopener noreferrer">Crea
|
<p>
|
||||||
una aquí.</a< /p>
|
¿No tienes cuenta de Nostr?
|
||||||
</body>
|
<a
|
||||||
|
href="https://start.njump.me/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>Crea una aquí.</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -1,20 +1,17 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
<head>
|
||||||
<head>
|
|
||||||
<title>Invite Details</title>
|
<title>Invite Details</title>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1>Invite Details</h1>
|
<h1>Invite Details</h1>
|
||||||
<p>Invite UUID: <%= invite.uuid %>
|
<p>Invite UUID: <%= invite.uuid %></p>
|
||||||
</p>
|
|
||||||
<h2>Wait right there buddy... that invite has been spent already!</h2>
|
<h2>Wait right there buddy... that invite has been spent already!</h2>
|
||||||
<form>
|
<form>
|
||||||
<button onclick="" disabled>Click to accept invite with Nostr</button>
|
<button onclick="" disabled>Click to accept invite with Nostr</button>
|
||||||
</form>
|
</form>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
<head>
|
||||||
<head>
|
|
||||||
<title>Private page</title>
|
<title>Private page</title>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1>Private page</h1>
|
<h1>Private page</h1>
|
||||||
<p>If you are here, it's because your npub is white listed, and your session cookie is related to it.</p>
|
<p>
|
||||||
</body>
|
If you are here, it's because your npub is white listed, and your session
|
||||||
|
cookie is related to it.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue