f868775861
**What** - Move packages for `next` version of admin to core repo **Other** - Since this PR introduces packages that depend on Vite 5, it also introduces @types/node@^20. We have never had a direct dependency on the types package for Node, and as far as I can see that has resulted in us using the types from Node.js@8, as those are a dependency of one of our dependencies. With the introduction of @types/node@^20, two of our packages had TS errors because they were using the NodeJS.Timer type, which was deprecated in Node.js@14. We should add specific @types/node packages to all our packages, but I haven't done so in this PR to keep it as clean as possible. - Q: @olivermrbl I've added the new packages to the ignore list for changeset, is this enough to prevent them from being published?
97 lines
2.5 KiB
TypeScript
97 lines
2.5 KiB
TypeScript
import { Server } from "http"
|
|
import { Socket } from "net"
|
|
import Timeout = NodeJS.Timeout
|
|
|
|
interface SocketState extends Socket {
|
|
_idle: boolean
|
|
_connectionId: number
|
|
}
|
|
|
|
export abstract class GracefulShutdownServer {
|
|
public isShuttingDown: boolean
|
|
public abstract shutdown(timeout?: number): Promise<void>
|
|
public static create<T extends Server>(
|
|
originalServer: T,
|
|
waitingResponseTime: number = 200
|
|
): T & GracefulShutdownServer {
|
|
let connectionId = 0
|
|
let shutdownPromise: Promise<void>
|
|
|
|
const allSockets: { [id: number]: SocketState } = {}
|
|
|
|
const server = originalServer as T & GracefulShutdownServer
|
|
server.isShuttingDown = false
|
|
server.shutdown = async (timeout: number = 0): Promise<void> => {
|
|
if (server.isShuttingDown) {
|
|
return shutdownPromise
|
|
}
|
|
|
|
server.isShuttingDown = true
|
|
|
|
shutdownPromise = new Promise((ok, nok) => {
|
|
let forceQuit = false
|
|
let cleanInterval: Timeout
|
|
|
|
try {
|
|
// stop accepting new incoming connections
|
|
server.close(() => {
|
|
clearInterval(cleanInterval)
|
|
ok()
|
|
})
|
|
|
|
if (+timeout > 0) {
|
|
setTimeout(() => {
|
|
forceQuit = true
|
|
}, timeout).unref()
|
|
}
|
|
|
|
cleanInterval = setInterval(() => {
|
|
if (!Object.keys(allSockets).length) {
|
|
clearInterval(cleanInterval)
|
|
}
|
|
|
|
for (const key of Object.keys(allSockets)) {
|
|
const socketId = +key
|
|
if (forceQuit || allSockets[socketId]._idle) {
|
|
allSockets[socketId].destroy()
|
|
delete allSockets[socketId]
|
|
}
|
|
}
|
|
}, waitingResponseTime)
|
|
} catch (error) {
|
|
clearInterval(cleanInterval!)
|
|
return nok(error)
|
|
}
|
|
})
|
|
|
|
return shutdownPromise
|
|
}
|
|
|
|
const onConnect = (originalSocket) => {
|
|
connectionId++
|
|
const socket = originalSocket as SocketState
|
|
socket._idle = true
|
|
socket._connectionId = connectionId
|
|
allSockets[connectionId] = socket
|
|
|
|
socket.on("close", () => {
|
|
delete allSockets[socket._connectionId]
|
|
})
|
|
}
|
|
|
|
server.on("connection", onConnect)
|
|
server.on("secureConnection", onConnect)
|
|
|
|
server.on("request", (req, res) => {
|
|
const customSocket = req.socket as SocketState
|
|
customSocket._idle = false
|
|
|
|
res.on("finish", () => {
|
|
customSocket._idle = true
|
|
})
|
|
})
|
|
|
|
return server
|
|
}
|
|
}
|