Add signature and authed_requests to database
This commit is contained in:
+1
-1
@@ -17,7 +17,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"analyze": "tsdown --clean --no-fixed-extension --sourcemap source/index.ts && esbuild-analyzer dist/",
|
"analyze": "tsdown --clean --no-fixed-extension --sourcemap source/index.ts && esbuild-analyzer dist/",
|
||||||
"build": "tsdown --clean --sourcemap source/index.ts",
|
"build": "tsdown --clean --sourcemap source/index.ts",
|
||||||
"dev": "tsx watch source/app.ts",
|
"dev": "tsx watch source/index.ts",
|
||||||
"docs": "typedoc --hideGenerator --categorizeByGroup",
|
"docs": "typedoc --hideGenerator --categorizeByGroup",
|
||||||
"format": "prettier --write . && eslint --fix",
|
"format": "prettier --write . && eslint --fix",
|
||||||
"spellcheck": "cspell 'source/**' 'test/**' 'playground/**'",
|
"spellcheck": "cspell 'source/**' 'test/**' 'playground/**'",
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ const configSchema = z.object({
|
|||||||
.object({
|
.object({
|
||||||
origin: z.string().default('*'),
|
origin: z.string().default('*'),
|
||||||
methods: z.array(z.string()).default([ 'GET', 'POST', 'PUT', 'DELETE', 'OPTIONS' ]),
|
methods: z.array(z.string()).default([ 'GET', 'POST', 'PUT', 'DELETE', 'OPTIONS' ]),
|
||||||
allowedHeaders: z.array(z.string()).default([ 'Content-Type', 'cache-control', 'X-Timestamp', 'X-PublicKey', 'X-Signature' ]),
|
allowedHeaders: z.array(z.string()).default([ 'Content-Type', 'cache-control', 'X-Timestamp', 'X-Public-Key', 'X-Signature' ]),
|
||||||
})
|
})
|
||||||
.partial()
|
.partial()
|
||||||
.prefault({}),
|
.prefault({}),
|
||||||
@@ -48,6 +48,11 @@ const configSchema = z.object({
|
|||||||
.int()
|
.int()
|
||||||
.positive()
|
.positive()
|
||||||
.default(5 * 60 * 1000),
|
.default(5 * 60 * 1000),
|
||||||
|
uniqueRequestCleanupIntervalMs: z.coerce
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.positive()
|
||||||
|
.default(10 * 60 * 1000),
|
||||||
})
|
})
|
||||||
.prefault({}),
|
.prefault({}),
|
||||||
});
|
});
|
||||||
@@ -83,6 +88,7 @@ export class Config {
|
|||||||
},
|
},
|
||||||
auth: {
|
auth: {
|
||||||
timestampWindowMs: process.env.AUTH_TIMESTAMP_WINDOW_MS ? Number(process.env.AUTH_TIMESTAMP_WINDOW_MS) : undefined,
|
timestampWindowMs: process.env.AUTH_TIMESTAMP_WINDOW_MS ? Number(process.env.AUTH_TIMESTAMP_WINDOW_MS) : undefined,
|
||||||
|
uniqueRequestCleanupIntervalMs: process.env.AUTH_UNIQUE_REQUEST_CLEANUP_INTERVAL_MS ? Number(process.env.AUTH_UNIQUE_REQUEST_CLEANUP_INTERVAL_MS) : undefined,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,19 @@ export const up = async (db: Kysely<DatabaseTables>): Promise<void> => {
|
|||||||
.addColumn('public_key', 'text', (col) => col.notNull())
|
.addColumn('public_key', 'text', (col) => col.notNull())
|
||||||
.addColumn('blob', 'blob', (col) => col.notNull())
|
.addColumn('blob', 'blob', (col) => col.notNull())
|
||||||
.addColumn('timestamp', 'integer', (col) => col.notNull().defaultTo(millisecondTime))
|
.addColumn('timestamp', 'integer', (col) => col.notNull().defaultTo(millisecondTime))
|
||||||
|
.addColumn('signature', 'text', (col) => col.notNull())
|
||||||
.addPrimaryKeyConstraint('pk_resource_data', [ 'resource_id', 'public_key' ])
|
.addPrimaryKeyConstraint('pk_resource_data', [ 'resource_id', 'public_key' ])
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
|
// Table for authed requests
|
||||||
|
// We will store the signature and the timestamp of the request, and we will clear out rows that are older than our msTimeout for our auth
|
||||||
|
await db.schema
|
||||||
|
.createTable('authed_requests')
|
||||||
|
.ifNotExists()
|
||||||
|
.addColumn('signature', 'text', (col) => col.notNull())
|
||||||
|
.addColumn('timestamp', 'integer', (col) => col.notNull().defaultTo(millisecondTime))
|
||||||
|
.addPrimaryKeyConstraint('pk_authed_requests', [ 'signature' ])
|
||||||
|
.execute();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,4 +46,7 @@ export const up = async (db: Kysely<DatabaseTables>): Promise<void> => {
|
|||||||
export const down = async (db: Kysely<DatabaseTables>): Promise<void> => {
|
export const down = async (db: Kysely<DatabaseTables>): Promise<void> => {
|
||||||
await db.schema.dropTable('resource_data').ifExists()
|
await db.schema.dropTable('resource_data').ifExists()
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
|
await db.schema.dropTable('authed_requests').ifExists()
|
||||||
|
.execute();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,9 +22,22 @@ export interface ResourceDataTable {
|
|||||||
|
|
||||||
/** Millisecond timestamp of the last write. */
|
/** Millisecond timestamp of the last write. */
|
||||||
timestamp: Timestamp;
|
timestamp: Timestamp;
|
||||||
|
|
||||||
|
/** Signature of the write. */
|
||||||
|
signature: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuthedRequestsTable {
|
||||||
|
|
||||||
|
/** Signature of the request. */
|
||||||
|
signature: string;
|
||||||
|
|
||||||
|
/** Millisecond timestamp of the request. */
|
||||||
|
timestamp: Timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Complete Kysely schema mapping for the sync server database. */
|
/** Complete Kysely schema mapping for the sync server database. */
|
||||||
export interface DatabaseTables {
|
export interface DatabaseTables {
|
||||||
resource_data: ResourceDataTable;
|
resource_data: ResourceDataTable;
|
||||||
|
authed_requests: AuthedRequestsTable;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const testConfigDefaultsTo1MiBRequestBodyLimit = (): void => {
|
|||||||
expect(config.server.host).toBe('0.0.0.0');
|
expect(config.server.host).toBe('0.0.0.0');
|
||||||
expect(config.server.cors.origin).toBe('*');
|
expect(config.server.cors.origin).toBe('*');
|
||||||
expect(config.server.cors.methods).toEqual([ 'GET', 'POST', 'PUT', 'DELETE', 'OPTIONS' ]);
|
expect(config.server.cors.methods).toEqual([ 'GET', 'POST', 'PUT', 'DELETE', 'OPTIONS' ]);
|
||||||
expect(config.server.cors.allowedHeaders).toEqual([ 'Content-Type', 'cache-control', 'X-Timestamp', 'X-PublicKey', 'X-Signature' ]);
|
expect(config.server.cors.allowedHeaders).toEqual([ 'Content-Type', 'cache-control', 'X-Timestamp', 'X-Public-Key', 'X-Signature' ]);
|
||||||
expect(config.auth.timestampWindowMs).toBe(300000);
|
expect(config.auth.timestampWindowMs).toBe(300000);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user