Browse Source

move metrics to general

metrics is not per interface
pull/1655/head
Bernd Storath 6 months ago
parent
commit
72feb1eb6d
  1. 11
      src/server/database/migrations/0000_short_skin.sql
  2. 4
      src/server/database/migrations/0001_classy_the_stranger.sql
  3. 77
      src/server/database/migrations/meta/0000_snapshot.json
  4. 79
      src/server/database/migrations/meta/0001_snapshot.json
  5. 4
      src/server/database/migrations/meta/_journal.json
  6. 7
      src/server/database/repositories/general/schema.ts
  7. 61
      src/server/database/repositories/general/service.ts
  8. 6
      src/server/database/repositories/interface/schema.ts
  9. 21
      src/server/database/repositories/metrics/schema.ts
  10. 31
      src/server/database/repositories/metrics/service.ts
  11. 4
      src/server/database/repositories/metrics/types.ts
  12. 1
      src/server/database/schema.ts
  13. 3
      src/server/database/sqlite.ts
  14. 2
      src/server/routes/metrics/json.get.ts
  15. 20
      src/server/utils/handler.ts

11
src/server/database/migrations/0000_short_skin.sql

@ -24,6 +24,9 @@ CREATE TABLE `general_table` (
`setupStep` integer NOT NULL,
`session_password` text NOT NULL,
`session_timeout` integer NOT NULL,
`metricsPrometheus` integer NOT NULL,
`metricsJson` integer NOT NULL,
`metricsPassword` text,
`created_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
`updated_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL
);
@ -54,14 +57,6 @@ CREATE TABLE `interfaces_table` (
);
--> statement-breakpoint
CREATE UNIQUE INDEX `interfaces_table_port_unique` ON `interfaces_table` (`port`);--> statement-breakpoint
CREATE TABLE `prometheus_table` (
`id` text PRIMARY KEY NOT NULL,
`password` text NOT NULL,
`created_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
`updated_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
FOREIGN KEY (`id`) REFERENCES `interfaces_table`(`name`) ON UPDATE cascade ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `one_time_links_table` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`one_time_link` text NOT NULL,

4
src/server/database/migrations/0001_classy_the_stranger.sql

@ -1,6 +1,6 @@
PRAGMA journal_mode=WAL;--> statement-breakpoint
INSERT INTO `general_table` (`setupStep`, `session_password`, `session_timeout`)
VALUES (1, hex(randomblob(256)), 3600);
INSERT INTO `general_table` (`setupStep`, `session_password`, `session_timeout`, `metricsPrometheus`, `metricsJson`)
VALUES (1, hex(randomblob(256)), 3600, 0, 0);
--> statement-breakpoint
INSERT INTO `interfaces_table` (`name`, `device`, `port`, `private_key`, `public_key`, `ipv4_cidr`, `ipv6_cidr`, `mtu`, `enabled`)
VALUES ('wg0', 'eth0', 51820, '---default---', '---default---', '10.8.0.0/24', 'fdcc:ad94:bacf:61a4::cafe:0/112', 1420, 1);

77
src/server/database/migrations/meta/0000_snapshot.json

@ -1,7 +1,7 @@
{
"version": "6",
"dialect": "sqlite",
"id": "25907c5f-be21-4ae6-88c4-1a72b2f335e7",
"id": "2c4694af-5916-430f-96d3-55aac2653e7e",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"clients_table": {
@ -175,6 +175,27 @@
"notNull": true,
"autoincrement": false
},
"metricsPrometheus": {
"name": "metricsPrometheus",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"metricsJson": {
"name": "metricsJson",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"metricsPassword": {
"name": "metricsPassword",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "text",
@ -370,60 +391,6 @@
"uniqueConstraints": {},
"checkConstraints": {}
},
"prometheus_table": {
"name": "prometheus_table",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"password": {
"name": "password",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(CURRENT_TIMESTAMP)"
},
"updated_at": {
"name": "updated_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(CURRENT_TIMESTAMP)"
}
},
"indexes": {},
"foreignKeys": {
"prometheus_table_id_interfaces_table_name_fk": {
"name": "prometheus_table_id_interfaces_table_name_fk",
"tableFrom": "prometheus_table",
"tableTo": "interfaces_table",
"columnsFrom": [
"id"
],
"columnsTo": [
"name"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"one_time_links_table": {
"name": "one_time_links_table",
"columns": {

79
src/server/database/migrations/meta/0001_snapshot.json

@ -1,6 +1,6 @@
{
"id": "60af732f-adc0-405d-96cc-2f818585f593",
"prevId": "25907c5f-be21-4ae6-88c4-1a72b2f335e7",
"id": "91d39ed5-2c45-4af6-ba39-4cd72ba71f6a",
"prevId": "2c4694af-5916-430f-96d3-55aac2653e7e",
"version": "6",
"dialect": "sqlite",
"tables": {
@ -175,6 +175,27 @@
"notNull": true,
"autoincrement": false
},
"metricsPrometheus": {
"name": "metricsPrometheus",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"metricsJson": {
"name": "metricsJson",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"metricsPassword": {
"name": "metricsPassword",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "text",
@ -370,60 +391,6 @@
"uniqueConstraints": {},
"checkConstraints": {}
},
"prometheus_table": {
"name": "prometheus_table",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"password": {
"name": "password",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(CURRENT_TIMESTAMP)"
},
"updated_at": {
"name": "updated_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(CURRENT_TIMESTAMP)"
}
},
"indexes": {},
"foreignKeys": {
"prometheus_table_id_interfaces_table_name_fk": {
"name": "prometheus_table_id_interfaces_table_name_fk",
"tableFrom": "prometheus_table",
"columnsFrom": [
"id"
],
"tableTo": "interfaces_table",
"columnsTo": [
"name"
],
"onUpdate": "cascade",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"one_time_links_table": {
"name": "one_time_links_table",
"columns": {

4
src/server/database/migrations/meta/_journal.json

@ -5,14 +5,14 @@
{
"idx": 0,
"version": "6",
"when": 1737122352401,
"when": 1739191645161,
"tag": "0000_short_skin",
"breakpoints": true
},
{
"idx": 1,
"version": "6",
"when": 1737122356601,
"when": 1739191678456,
"tag": "0001_classy_the_stranger",
"breakpoints": true
}

7
src/server/database/repositories/general/schema.ts

@ -3,9 +3,16 @@ import { sqliteTable, text, int } from 'drizzle-orm/sqlite-core';
export const general = sqliteTable('general_table', {
id: int().primaryKey({ autoIncrement: false }).default(1),
setupStep: int().notNull(),
sessionPassword: text('session_password').notNull(),
sessionTimeout: int('session_timeout').notNull(),
metricsPrometheus: int({ mode: 'boolean' }).notNull(),
metricsJson: int({ mode: 'boolean' }).notNull(),
metricsPassword: text(),
createdAt: text('created_at')
.notNull()
.default(sql`(CURRENT_TIMESTAMP)`),

61
src/server/database/repositories/general/service.ts

@ -5,7 +5,30 @@ import type { GeneralUpdateType } from './types';
function createPreparedStatement(db: DBType) {
return {
find: db.query.general.findFirst().prepare(),
getSetupStep: db.query.general
.findFirst({
columns: {
setupStep: true,
},
})
.prepare(),
getSessionConfig: db.query.general
.findFirst({
columns: {
sessionPassword: true,
sessionTimeout: true,
},
})
.prepare(),
getMetricsConfig: db.query.general
.findFirst({
columns: {
metricsPrometheus: true,
metricsJson: true,
metricsPassword: true,
},
})
.prepare(),
updateSetupStep: db
.update(general)
.set({
@ -31,19 +54,13 @@ export class GeneralService {
/**
* @throws
*/
private async get() {
const result = await this.#statements.find.execute();
async getSetupStep() {
const result = await this.#statements.getSetupStep.execute();
if (!result) {
throw new Error('General Config not found');
}
return result;
}
/**
* @throws
*/
async getSetupStep() {
const result = await this.get();
return { step: result.setupStep, done: result.setupStep === 0 };
}
@ -55,13 +72,35 @@ export class GeneralService {
* @throws
*/
async getSessionConfig() {
const result = await this.get();
const result = await this.#statements.getSessionConfig.execute();
if (!result) {
throw new Error('General Config not found');
}
return {
sessionPassword: result.sessionPassword,
sessionTimeout: result.sessionTimeout,
};
}
/**
* @throws
*/
async getMetricsConfig() {
const result = await this.#statements.getMetricsConfig.execute();
if (!result) {
throw new Error('General Config not found');
}
return {
prometheus: result.metricsPrometheus,
json: result.metricsJson,
password: result.metricsPassword,
};
}
update(data: GeneralUpdateType) {
return this.#statements.update.execute(data);
}

6
src/server/database/repositories/interface/schema.ts

@ -1,7 +1,7 @@
import { sql, relations } from 'drizzle-orm';
import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { userConfig, hooks, prometheus } from '../../schema';
import { userConfig, hooks } from '../../schema';
// maybe support multiple interfaces in the future
export const wgInterface = sqliteTable('interfaces_table', {
@ -28,10 +28,6 @@ export const wgInterfaceRelations = relations(wgInterface, ({ one }) => ({
fields: [wgInterface.name],
references: [hooks.id],
}),
prometheus: one(prometheus, {
fields: [wgInterface.name],
references: [prometheus.id],
}),
userConfig: one(userConfig, {
fields: [wgInterface.name],
references: [userConfig.id],

21
src/server/database/repositories/metrics/schema.ts

@ -1,21 +0,0 @@
import { sql } from 'drizzle-orm';
import { sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { wgInterface } from '../../schema';
export const prometheus = sqliteTable('prometheus_table', {
id: text()
.primaryKey()
.references(() => wgInterface.name, {
onDelete: 'cascade',
onUpdate: 'cascade',
}),
password: text().notNull(),
createdAt: text('created_at')
.notNull()
.default(sql`(CURRENT_TIMESTAMP)`),
updatedAt: text('updated_at')
.notNull()
.default(sql`(CURRENT_TIMESTAMP)`)
.$onUpdate(() => sql`(CURRENT_TIMESTAMP)`),
});

31
src/server/database/repositories/metrics/service.ts

@ -1,31 +0,0 @@
import type { DBType } from '#db/sqlite';
import { eq, sql } from 'drizzle-orm';
import { prometheus } from './schema';
function createPreparedStatement(db: DBType) {
return {
get: db.query.prometheus
.findFirst({ where: eq(prometheus.id, sql.placeholder('interface')) })
.prepare(),
};
}
export class PrometheusService {
#statements: ReturnType<typeof createPreparedStatement>;
constructor(db: DBType) {
this.#statements = createPreparedStatement(db);
}
get(infName: string) {
return this.#statements.get.execute({ interface: infName });
}
}
export class MetricsService {
prometheus: PrometheusService;
constructor(db: DBType) {
this.prometheus = new PrometheusService(db);
}
}

4
src/server/database/repositories/metrics/types.ts

@ -1,4 +0,0 @@
import type { InferSelectModel } from 'drizzle-orm';
import type { prometheus } from './schema';
export type PrometheusType = InferSelectModel<typeof prometheus>;

1
src/server/database/schema.ts

@ -3,7 +3,6 @@ export * from './repositories/client/schema';
export * from './repositories/general/schema';
export * from './repositories/hooks/schema';
export * from './repositories/interface/schema';
export * from './repositories/metrics/schema';
export * from './repositories/oneTimeLink/schema';
export * from './repositories/user/schema';
export * from './repositories/userConfig/schema';

3
src/server/database/sqlite.ts

@ -11,7 +11,6 @@ import { UserConfigService } from './repositories/userConfig/service';
import { InterfaceService } from './repositories/interface/service';
import { HooksService } from './repositories/hooks/service';
import { OneTimeLinkService } from './repositories/oneTimeLink/service';
import { MetricsService } from './repositories/metrics/service';
const DB_DEBUG = debug('Database');
@ -31,7 +30,6 @@ class DBService {
interfaces: InterfaceService;
hooks: HooksService;
oneTimeLinks: OneTimeLinkService;
metrics: MetricsService;
constructor(db: DBType) {
this.clients = new ClientService(db);
@ -41,7 +39,6 @@ class DBService {
this.interfaces = new InterfaceService(db);
this.hooks = new HooksService(db);
this.oneTimeLinks = new OneTimeLinkService(db);
this.metrics = new MetricsService(db);
}
}

2
src/server/routes/metrics/json.get.ts

@ -1,4 +1,4 @@
export default defineMetricsHandler('prometheus', async () => {
export default defineMetricsHandler('json', async () => {
return getMetricsJSON();
});

20
src/server/utils/handler.ts

@ -58,7 +58,7 @@ export const defineSetupEventHandler = <
});
};
type Metrics = 'prometheus';
type Metrics = 'prometheus' | 'json';
type MetricsHandler<
TReq extends EventHandlerRequest,
@ -94,22 +94,24 @@ export const defineMetricsHandler = <
});
}
const metricsConfig = await Database.metrics[type].get('wg0');
const metricsConfig = await Database.general.getMetricsConfig();
if (!metricsConfig) {
if (metricsConfig[type] !== true) {
throw createError({
statusCode: 400,
statusMessage: 'Metrics not enabled',
});
}
const tokenValid = await isPasswordValid(value, metricsConfig.password);
if (metricsConfig.password) {
const tokenValid = await isPasswordValid(value, metricsConfig.password);
if (!tokenValid) {
throw createError({
statusCode: 401,
statusMessage: 'Incorrect token',
});
if (!tokenValid) {
throw createError({
statusCode: 401,
statusMessage: 'Incorrect token',
});
}
}
return await handler({ event });

Loading…
Cancel
Save