work on security features, logger, snowflakes, http servers
This commit is contained in:
41
src/http/cookies.ts
Normal file
41
src/http/cookies.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
|
||||
import { FastifyReply, FastifyRequest } from 'fastify';
|
||||
|
||||
export type SameSite = 'Strict' | 'Lax' | 'None';
|
||||
|
||||
export function set_cookie(res: FastifyReply, name: string, value: string, expires: Date, secure: boolean, same_site: SameSite = 'Strict', path?: string) {
|
||||
const cookie
|
||||
= `${name}=${value}; `
|
||||
+ `Expires=${expires.toUTCString()}; `
|
||||
+ (path ? ` Path=${path}; ` : '')
|
||||
+ `HttpOnly; `
|
||||
+ `SameSite=${same_site};`
|
||||
+ (secure ? ' Secure;' : '')
|
||||
;
|
||||
|
||||
res.header('set-cookie', cookie);
|
||||
}
|
||||
|
||||
export function invalidate_cookie(res: FastifyReply, name: string, secure: boolean, path?: string) {
|
||||
set_cookie(res, name, 'invalidate', new Date(0), secure, 'Strict', path);
|
||||
}
|
||||
|
||||
export function parse_req_cookies(req: FastifyRequest) : Record<string, string> {
|
||||
const result: Record<string, string> = { };
|
||||
const cookies = req.headers.cookie || '';
|
||||
|
||||
for (const cookie of cookies.split(';')) {
|
||||
const index = cookie.indexOf('=');
|
||||
|
||||
if (index < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const name = cookie.slice(0, index).trim();
|
||||
const value = cookie.slice(index + 1).trim();
|
||||
|
||||
result[name] = decodeURIComponent(value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
85
src/http/parse-cache-headers.ts
Normal file
85
src/http/parse-cache-headers.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
|
||||
import { DateTime } from 'luxon';
|
||||
import { IncomingHttpHeaders } from 'http';
|
||||
|
||||
export interface ParsedCacheHeaders {
|
||||
age?: number;
|
||||
etag?: string;
|
||||
vary?: string[];
|
||||
date?: DateTime;
|
||||
cache_control?: CacheControl;
|
||||
expires?: DateTime;
|
||||
last_modified?: DateTime;
|
||||
}
|
||||
|
||||
export interface CacheControl {
|
||||
private?: boolean;
|
||||
no_store?: boolean;
|
||||
no_cache?: boolean;
|
||||
max_age?: number;
|
||||
must_revalidate?: boolean;
|
||||
proxy_revalidate?: boolean;
|
||||
}
|
||||
|
||||
export function parse_cache_headers(headers: IncomingHttpHeaders) {
|
||||
const result: ParsedCacheHeaders = { };
|
||||
|
||||
if (headers['age']) {
|
||||
result.age = parseInt(headers['age'], 10);
|
||||
}
|
||||
|
||||
if (headers['etag']) {
|
||||
result.etag = headers['etag'];
|
||||
}
|
||||
|
||||
if (headers['vary']) {
|
||||
result.vary = headers['vary'].split(',').map((str) => str.trim().toLowerCase());
|
||||
}
|
||||
|
||||
if (headers['date']) {
|
||||
result.date = DateTime.fromHTTP(headers['date']);
|
||||
}
|
||||
|
||||
if (headers['cache-control']) {
|
||||
result.cache_control = { };
|
||||
|
||||
for (let directive of headers['cache-control'].split(',')) {
|
||||
directive = directive.trim();
|
||||
|
||||
switch (directive) {
|
||||
case 'private':
|
||||
result.cache_control.private = true;
|
||||
break;
|
||||
case 'no-store':
|
||||
result.cache_control.no_store = true;
|
||||
break;
|
||||
case 'no-cache':
|
||||
result.cache_control.no_cache = true;
|
||||
break;
|
||||
case 'must-revalidate':
|
||||
result.cache_control.must_revalidate = true;
|
||||
break;
|
||||
case 'proxy-revalidate':
|
||||
result.cache_control.proxy_revalidate = true;
|
||||
break;
|
||||
default:
|
||||
if (directive.startsWith('max-age=')) {
|
||||
result.cache_control.max_age = parseInt(directive.slice(8), 10);
|
||||
break;
|
||||
}
|
||||
|
||||
// todo: log something here about unknown directive
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (headers['expires']) {
|
||||
result.expires = DateTime.fromHTTP(headers['expires']);
|
||||
}
|
||||
|
||||
if (headers['last-modified']) {
|
||||
result.last_modified = DateTime.fromHTTP(headers['last-modified']);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
12
src/http/request.ts
Normal file
12
src/http/request.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
import { FastifyRequest } from 'fastify';
|
||||
import { RouteGenericInterface } from 'fastify/types/route';
|
||||
import { SessionKey } from '../security/session-key';
|
||||
import { SessionData } from '../storage';
|
||||
|
||||
export type Req<T = RouteGenericInterface> = FastifyRequest<T> & {
|
||||
session?: {
|
||||
key: SessionKey;
|
||||
data: SessionData;
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user