koa session expire event - koa

Am trying to log a message when a koa session expires. But adding a listener for the expire event doesnt seem to work for me.
Following is the sample code. Set 2 mins as maxAge for the session.
import koa from 'koa';
import serve from 'koa-static';
import route from 'koa-route';
import session from 'koa-session';
import mount from 'koa-mount';
let pub = new koa();
let parse = new koa();
let app = new koa();
app.keys = ['test'];
const CONFIG = {
key: 'sess',
maxAge: 2000
};
parse.use(ctx => {
// ignore favicon
if (ctx.path === '/test') return;
let n = ctx.session.views || 0;
ctx.session.views = ++n;
ctx.body = n + ' views';
console.log(ctx.session);
});
pub.use(serve('./public'));
app.use(session(CONFIG,app));
app.on('expired',(key,value,ctx) => console.log(key));
app.use(mount('/',pub));
app.use(mount('/parse',parse));
app.listen(8080);
Here everytime i refresh the page after 2 seconds in browser i can see the "views" value set back to 1 meaning the previous session had expired. But the log statement as part of the expired event is not getting hit. May be the way am adding the event listener is wrong. Can someone please help?

After having a look at library files, koajs/session -> context.js
if (value._expire && value._expire < Date.now()) {
debug('expired session');
this.emit('expired', { key, value, ctx });
return false;
}
where emit is:
emit(event, data) {
setImmediate(() => {
this.app.emit(`session:${event}`, data);
});
}
So I think in your code you shouldchange the name you are trying to capture the event with:
app.on('session:expired',(key,value,ctx) => console.log(key));
If key is containing a value, you should see something printing out in the console :)

Related

How do I use next auth getServerSession in next js 13 beta server component in app directory

I'm using next auth v4 with next js 13 beta with server component, everything working fine. But I have a situation where I will need to know the logged user id, since I'm using next auth, I have access to session, I can use useSession() but then I will need to make the component a client component, So I want to use it on server, I can use getServerSession in api since I have access to req & res object, but in next js beta with new app dir, I can't do it. Please let me know if you know how to fix the issue. Thank you
import { getServerSession } from "next-auth";
import { authOptions } from "#/pages/api/auth/[...nextauth]";
const Test = async () => {
const user_id = 1; // How do I get user id from session, user_id is available in session
// I don't have access req & res object in server component.
const data = await getServerSession(request, response, authOptions);
console.log(data);
});
return (
<></>
);
};
export default Test;
Didn't find enough information
I found an answer, in next js 13 beta, you wont need to use request & response object, just use the authOptions, it will work
import { getServerSession } from "next-auth";
import { authOptions } from "#/pages/api/auth/[...nextauth]";
const Test = async () => {
const data = await getServerSession(authOptions);
console.log(data);
});
return (
<></>
);
};
export default Test;

IndexedDB breaks in Firefox after trying to save autoIncremented Blob

I am trying to implement Blob storage via IndexedDB for long Media recordings.
My code works fine in Chrome and Edge (not tested in Safari yet) - but won't do anything in Firefox. There are no errors, it just doesn't try to fulfill my requests past the initial DB Connection (which is successful). Intuitively, it seems that the processing is blocked by something. But I don't have anything in my code which would be blocking.
Simplified version of the code (without heavy logging and excessive error checks which I have added trying to debug):
const dbName = 'recording'
const storeValue = 'blobs'
let connection = null
const handler = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB
function connect() {
return new Promise((resolve, reject) => {
const request = handler.open(dbName)
request.onupgradeneeded = (event) => {
const db = event.target.result
if (db.objectStoreNames.contains(storeValue)) {
db.deleteObjectStore(storeValue)
}
db.createObjectStore(storeValue, {
keyPath: 'id',
autoIncrement: true,
})
}
request.onerror = () => {
reject()
}
request.onsuccess = () => {
connection = request.result
connection.onerror = () => {
connection = null
}
connection.onclose = () => {
connection = null
}
resolve()
}
})
}
async function saveChunk(chunk) {
if (!connection) await connect()
return new Promise((resolve, reject) => {
const store = connection.transaction(
storeValue,
'readwrite'
).objectStore(storeValue)
const req = store.add(chunk)
req.onsuccess = () => {
console.warn('DONE!') // Fires in Chrome and Edge - not in Firefox
resolve(req.result)
}
req.onerror = () => {
reject()
}
req.transaction.oncomplete = () => {
console.warn('DONE!') // Fires in Chrome and Edge - not in Firefox
}
})
}
// ... on blob available
await saveChunk(blob)
What I tried so far:
close any other other browser windows, anything that could count as on "open connection" that might be blocking execution
refresh Firefox profile
let my colleague test the code on his own machine => same result
Additional information that might useful:
Running in Nuxt 2.15.8 dev environment (localhost:3000). Code is used in the component as a Mixin. The project is rather large and uses a bunch of different browser APIs. There might be some kind of collision ?! This is the only place where we use IndexedDB, though, so to get to the bottom of this without any errors being thrown seems almost impossible.
Edit:
When I create a brand new Database, there is a brief window in which Transactions complete fine, but after some time has passed/something triggered, it goes back to being queued indefinitely.
I found out this morning when I had this structure:
...
clearDatabase() {
// get the store
const req = store.clear()
req.transaction.oncomplete = () => console.log('all good!')
}
await this.connect()
await this.clearDatabase()
'All good' fired. But any subsequent requests were broken same as before.
On page reload, even the clearDatabase request was broken again.
Something breaks with ongoing usage.
Edit2:
It's clearly connected to saving a Blob instance without an id with the autoIncrement option. Not only does it fail silently, it basically completely corrupts the DB. If I manually assign an incrementing ID to a Blob object, it works! If I leave out the id field for a regular simple object, it also works! Anyone knows about this? I feel like saving blobs is a common use-case so this should have been found already?!
I've concluded, unless proven otherwise, that it's a Firefox bug and opened a ticket on Bugzilla.
This happens with Blobs but might also be true for other instances. If you find yourself in the same situation there is a workaround. Don't rely on autoIncrement and assign IDs manually before trying to save them to the DB.

Shopify App Rejected Due To Redirecting To A Different Page When Attempting To Install Your App in incognito

I have created a Shopify public app using node with Shopify CLI, So after i have submitted the app for the reviewing in the Shopify app store.
So the review team rejected the app mentioning the app is not directed to the oAuth page when we install the app ( in Incognito ) window in normal browser their is not mush issue.
The redirecting page in incognito
The issue that point out buy the ShopifyThe replay mail from the review team
The issue only occur in the incognito tab, ie when we log in to our partner dashboard from the Incognito and select the app and choose the "test on development store" option then the issue of select the account to continue will appear.
In normal mode of the browser it will directed to the oAuth page.
because of this reason the review team rejected the app.
Server.js
import "#babel/polyfill";
import dotenv from "dotenv";
import "isomorphic-fetch";
import createShopifyAuth, { verifyRequest } from "#shopify/koa-shopify-auth";
import Shopify, { ApiVersion } from "#shopify/shopify-api";
import Koa from "koa";
import next from "next";
import Router from "koa-router";
dotenv.config();
const port = parseInt(process.env.PORT, 10) || 8081;
const dev = process.env.NODE_ENV !== "production";
const app = next({
dev,
});
const handle = app.getRequestHandler();
Shopify.Context.initialize({
API_KEY: process.env.SHOPIFY_API_KEY,
API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
SCOPES: process.env.SCOPES.split(","),
HOST_NAME: process.env.HOST.replace(/https:\/\/|\/$/g, ""),
API_VERSION: ApiVersion.October20,
IS_EMBEDDED_APP: true,
// This should be replaced with your preferred storage strategy
SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
});
// Storing the currently active shops in memory will force them to re-login when your server restarts. You should
// persist this object in your app.
const ACTIVE_SHOPIFY_SHOPS = {};
app.prepare().then(async () => {
const server = new Koa();
const router = new Router();
server.keys = [Shopify.Context.API_SECRET_KEY];
server.use(
createShopifyAuth({
async afterAuth(ctx) {
// Access token and shop available in ctx.state.shopify
const { shop, accessToken, scope } = ctx.state.shopify;
const host = ctx.query.host;
ACTIVE_SHOPIFY_SHOPS[shop] = scope;
const response = await Shopify.Webhooks.Registry.register({
shop,
accessToken,
path: "/webhooks",
topic: "APP_UNINSTALLED",
webhookHandler: async (topic, shop, body) =>
delete ACTIVE_SHOPIFY_SHOPS[shop],
});
if (!response.success) {
console.log(
`Failed to register APP_UNINSTALLED webhook: ${response.result}`
);
}
// Redirect to app with shop parameter upon auth
ctx.redirect(`/?shop=${shop}&host=${host}`);
},
})
);
const handleRequest = async (ctx) => {
await handle(ctx.req, ctx.res);
ctx.respond = false;
ctx.res.statusCode = 200;
};
router.post("/webhooks", async (ctx) => {
try {
await Shopify.Webhooks.Registry.process(ctx.req, ctx.res);
console.log(`Webhook processed, returned status code 200`);
} catch (error) {
console.log(`Failed to process webhook: ${error}`);
}
});
router.post(
"/graphql",
verifyRequest({ returnHeader: true }),
async (ctx, next) => {
await Shopify.Utils.graphqlProxy(ctx.req, ctx.res);
}
);
router.get("(/_next/static/.*)", handleRequest); // Static content is clear
router.get("/_next/webpack-hmr", handleRequest); // Webpack content is clear
router.get("(.*)", async (ctx) => {
const shop = ctx.query.shop;
// This shop hasn't been seen yet, go through OAuth to create a session
if (ACTIVE_SHOPIFY_SHOPS[shop] === undefined) {
// const redirectUri = process.env.HOST + '/auth/callback';
// ctx.redirect(`https://${shop}/admin/oauth/authorize? //client_id=${process.env.SHOPIFY_API_KEY}&scope=${process.env.SCOPES}&state=$//{nonce}&redirect_uri=${redirectUri}`);
ctx.redirect(`/auth?shop=${shop}`);
} else {
await handleRequest(ctx);
}
});
server.use(router.allowedMethods());
server.use(router.routes());
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`);
});
});
If you guys can share your thoughts on this it will be very helpful.
Thanks.

Why is my koa application timing out when testing a route with Jest/Supertest

Summary of Problem
Receiving : Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Timeout when trying to run a test with Jest and supertest.
Specs
Koa2 project, Jest/Supertest testing, Babel 7.9.0 recommended configuration
What I've tried
I have a simple test from the same file running which I omitted from the code below for brevity. I've also tried sending an HTTP request from the browser - this file is imported & 'listen'ed in a server file. The request is failing because it is blocked by a CORS policy - I think this is a problem for another day and isn't affecting my test timing out.
I also tried removed .callback() from the supertest(..) call:
const response = await supertest(app).post('/save-material');
at which point I get TypeError: app.dress is not a function.
Here is the content of my test file:
process.env.NODE_ENV = 'test';
const app = require('../../src/server/app.js')
const supertest = require('supertest')
test('save-material returns response', async() => {
const response = await supertest(app.callback()).post('/save-material');
expect(response.status).toBe(200);
expect(response.body.status).toBe('success');
expect(response.body.msg).toBe('Material saved')
});
Here is the content of the imported file (app.js) from above:
require('#babel/register'); // not entry point - but is entry point for some tests
const Koa = require('koa');
var Router = require('koa-router')
const app = new Koa();
const router = new Router();
router
.post('/save-material', async(ctx) => {
ctx.response = {
status: 'success',
msg: 'Material saved'
}
return ctx;
})
app.use(router.routes());
app.use(router.allowedMethods());
module.exports = app;

MS Bot Framework (Nodejs): Session lifetime and delayed messages

This is my first try with Bot Framework (Nodejs). I want to test delayed messages, for example, my bot must answer after a 5 seconds after receiving the message.
So I tried with this code:
var builder = require('botbuilder');
var connector = new builder.consoleconnector().listen();
var bot = new builder.universalbot(connector);
bot.dialog('/', function (session) {
if (!session.userData.TimeoutStarted) {
session.send("I'll answer in 5 seconds");
session.userData.TimeoutStarted = true;
setTimeout(function() {
session.send("Answer after 5 seconds");
session.userData.TimeoutStarted = false;
}, 5000);
} else {
session.send("Bot is busy");
}
});
But this doesn't work. Callback function inside setTimeout fires, but all operations with session doesn't work at all.
So, I find possible solution here: How to send message later in bot framework and rewrite my code:
var builder = require('botbuilder');
var connector = new builder.ConsoleConnector().listen();
var bot = new builder.UniversalBot(connector);
bot.dialog('/', function (session) {
if (session.userData.Timeout > 0 && Date.now() - session.userData.Timeout > 5000)
session.userData.Timeout = 0;
if (!session.userData.Timeout) {
session.send("I'll answer in 5 seconds");
var reply = session.message;
setTimeout(function() {
reply.text = "Answer after 5 seconds";
bot.send(reply);
}, 5000);
session.userData.Timeout = Date.now();
} else {
session.send("Bot is busy");
}
});
This code works, but looks terrible with so many checks. So I have a few questions:
Why first code example doesn't work? I guess problem in the session lifetime and then what is session lifetime?
How to set session.userData in this examples? So In first code example I want to set it inside callback function inside setTimeout but it doesn't work too.
What is the best way to create delayed answers?
I just investigated this issue. Looks like there's a bug in ConsoleConnector that makes it impossible to send two messages using the same session object (above a given interval between messages, due to internal batching). As the state is also persisted during send, your delayed state update will also not work. If you added a call to session.save to your callback in setTimeout, it would persist the new state (but would still not send the message).
I believe your first example should work with ChatConnector (haven't had the chance to try though). I'll create a pull request with the fix to ConsoleConnector.
I hope this answers all your questions.
UPDATE
See this issue and the related pull request for more details.
UPDATE2
It works for me with ChatConnector, using this code:
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
});
var connector = new builder.ChatConnector({
appId: '',
appPassword: ''
});
var bot = new builder.UniversalBot(connector);
server.post('/api/messages', connector.listen());
bot.dialog('/', function (session) {
if (!session.userData.TimeoutStarted) {
session.send("I'll answer in 5 seconds");
session.userData.TimeoutStarted = true;
setTimeout(function() {
session.send("Answer after 5 seconds");
session.userData.TimeoutStarted = false;
}, 5000);
} else {
session.send("Bot is busy");
}
});
For people who are interested in sending a delayed message from bot, you could use session.delay(<ms>)
For example,
session.send('msg')
session.delay(5000) // delay 5 seconds
session.endDialog()

Resources