mirror of https://github.com/wg-easy/wg-easy
Browse Source
* start drizzle migration * split schema * improve schema * improve schema, cascade, unique * improve structure, start migration * migrate to sqlite * work in prod docker * start adding a better permission handler * permission matrix, permission handler * update packages * move session timeout to session config, use new permission handler * improve docker dev only install dependencies if changed * implement setup * migrate to sqlite * improve debug, fix custom migration * migrate to sqlite * regenerate migrations * ignore autogenerated migrations from prettier * migrate to sqlite * migrate to sqlite * Migrate to sqlite * fix prod error * move nuxt middleware from server to nuxt * update corepack in prod dockerfile * use correct branch for workflow * make docker file build on armv6/v7 * fix client update * update zod locales * cancel pr workflow if new commit * test concurrencypull/1648/head
committed by
Bernd Storath
117 changed files with 5437 additions and 3096 deletions
@ -0,0 +1 @@ |
|||||
|
public-hoist-pattern[]=@libsql/linux* |
@ -1 +1,2 @@ |
|||||
pnpm-lock.yaml |
pnpm-lock.yaml |
||||
|
server/database/migrations/meta |
||||
|
@ -0,0 +1,28 @@ |
|||||
|
export default defineNuxtRouteMiddleware(async (to) => { |
||||
|
// api & setup handled server side
|
||||
|
if (to.path.startsWith('/api/') || to.path.startsWith('/setup')) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
const authStore = useAuthStore(); |
||||
|
const userData = await authStore.getSession(); |
||||
|
|
||||
|
// skip login if already logged in
|
||||
|
if (to.path === '/login') { |
||||
|
if (userData?.username) { |
||||
|
return navigateTo('/', { redirectCode: 302 }); |
||||
|
} |
||||
|
return; |
||||
|
} |
||||
|
// Require auth for every page other than Login
|
||||
|
if (!userData?.username) { |
||||
|
return navigateTo('/login', { redirectCode: 302 }); |
||||
|
} |
||||
|
|
||||
|
// Check for admin access
|
||||
|
if (to.path.startsWith('/admin')) { |
||||
|
if (userData.role !== roles.ADMIN) { |
||||
|
return abortNavigation('Not allowed to access Admin Panel'); |
||||
|
} |
||||
|
} |
||||
|
}); |
@ -0,0 +1,10 @@ |
|||||
|
import { defineConfig } from 'drizzle-kit'; |
||||
|
|
||||
|
export default defineConfig({ |
||||
|
out: './server/database/migrations', |
||||
|
schema: './server/database/schema.ts', |
||||
|
dialect: 'sqlite', |
||||
|
dbCredentials: { |
||||
|
url: 'file:./wg0.db', |
||||
|
}, |
||||
|
}); |
File diff suppressed because it is too large
@ -1,4 +1,6 @@ |
|||||
export default defineEventHandler(async () => { |
export default definePermissionEventHandler(actions.ADMIN, async () => { |
||||
const system = await Database.system.get(); |
const sessionConfig = await Database.general.getSessionConfig(); |
||||
return system.general; |
return { |
||||
|
sessionTimeout: sessionConfig.sessionTimeout, |
||||
|
}; |
||||
}); |
}); |
||||
|
@ -1,8 +1,13 @@ |
|||||
export default defineEventHandler(async (event) => { |
import { GeneralUpdateSchema } from '#db/repositories/general/types'; |
||||
const data = await readValidatedBody( |
|
||||
event, |
export default definePermissionEventHandler( |
||||
validateZod(generalUpdateType, event) |
actions.ADMIN, |
||||
); |
async ({ event }) => { |
||||
await Database.system.updateGeneral(data); |
const data = await readValidatedBody( |
||||
return { success: true }; |
event, |
||||
}); |
validateZod(GeneralUpdateSchema, event) |
||||
|
); |
||||
|
await Database.general.update(data); |
||||
|
return { success: true }; |
||||
|
} |
||||
|
); |
||||
|
@ -1,4 +1,7 @@ |
|||||
export default defineEventHandler(async () => { |
export default definePermissionEventHandler(actions.ADMIN, async () => { |
||||
const system = await Database.system.get(); |
const hooks = await Database.hooks.get('wg0'); |
||||
return system.hooks; |
if (!hooks) { |
||||
|
throw new Error('Hooks not found'); |
||||
|
} |
||||
|
return hooks; |
||||
}); |
}); |
||||
|
@ -1,9 +1,14 @@ |
|||||
export default defineEventHandler(async (event) => { |
import { HooksUpdateSchema } from '#db/repositories/hooks/types'; |
||||
const data = await readValidatedBody( |
|
||||
event, |
export default definePermissionEventHandler( |
||||
validateZod(hooksUpdateType, event) |
actions.ADMIN, |
||||
); |
async ({ event }) => { |
||||
await Database.system.updateHooks(data); |
const data = await readValidatedBody( |
||||
await WireGuard.saveConfig(); |
event, |
||||
return { success: true }; |
validateZod(HooksUpdateSchema, event) |
||||
}); |
); |
||||
|
await Database.hooks.update('wg0', data); |
||||
|
await WireGuard.saveConfig(); |
||||
|
return { success: true }; |
||||
|
} |
||||
|
); |
||||
|
@ -1,8 +0,0 @@ |
|||||
export default defineEventHandler(async (event) => { |
|
||||
const { host, port } = await readValidatedBody( |
|
||||
event, |
|
||||
validateZod(hostPortType, event) |
|
||||
); |
|
||||
await Database.system.updateClientsHostPort(host, port); |
|
||||
return { success: true }; |
|
||||
}); |
|
@ -1,4 +0,0 @@ |
|||||
export default defineEventHandler(async () => { |
|
||||
const system = await Database.system.get(); |
|
||||
return system.interface; |
|
||||
}); |
|
@ -1,9 +0,0 @@ |
|||||
export default defineEventHandler(async (event) => { |
|
||||
const data = await readValidatedBody( |
|
||||
event, |
|
||||
validateZod(interfaceUpdateType, event) |
|
||||
); |
|
||||
await Database.system.updateInterface(data); |
|
||||
await WireGuard.saveConfig(); |
|
||||
return { success: true }; |
|
||||
}); |
|
@ -0,0 +1,15 @@ |
|||||
|
import { InterfaceCidrUpdateSchema } from '#db/repositories/interface/types'; |
||||
|
|
||||
|
export default definePermissionEventHandler( |
||||
|
actions.ADMIN, |
||||
|
async ({ event }) => { |
||||
|
const data = await readValidatedBody( |
||||
|
event, |
||||
|
validateZod(InterfaceCidrUpdateSchema, event) |
||||
|
); |
||||
|
|
||||
|
await Database.interfaces.updateCidr('wg0', data); |
||||
|
await WireGuard.saveConfig(); |
||||
|
return { success: true }; |
||||
|
} |
||||
|
); |
@ -0,0 +1,12 @@ |
|||||
|
export default definePermissionEventHandler(actions.ADMIN, async () => { |
||||
|
const wgInterface = await Database.interfaces.get('wg0'); |
||||
|
|
||||
|
if (!wgInterface) { |
||||
|
throw new Error('Interface not found'); |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
...wgInterface, |
||||
|
privateKey: undefined, |
||||
|
}; |
||||
|
}); |
@ -0,0 +1,14 @@ |
|||||
|
import { InterfaceUpdateSchema } from '#db/repositories/interface/types'; |
||||
|
|
||||
|
export default definePermissionEventHandler( |
||||
|
actions.ADMIN, |
||||
|
async ({ event }) => { |
||||
|
const data = await readValidatedBody( |
||||
|
event, |
||||
|
validateZod(InterfaceUpdateSchema, event) |
||||
|
); |
||||
|
await Database.interfaces.update('wg0', data); |
||||
|
await WireGuard.saveConfig(); |
||||
|
return { success: true }; |
||||
|
} |
||||
|
); |
@ -0,0 +1,7 @@ |
|||||
|
export default definePermissionEventHandler(actions.ADMIN, async () => { |
||||
|
const userConfig = await Database.userConfigs.get('wg0'); |
||||
|
if (!userConfig) { |
||||
|
throw new Error('User config not found'); |
||||
|
} |
||||
|
return userConfig; |
||||
|
}); |
@ -0,0 +1,14 @@ |
|||||
|
import { UserConfigUpdateSchema } from '#db/repositories/userConfig/types'; |
||||
|
|
||||
|
export default definePermissionEventHandler( |
||||
|
actions.ADMIN, |
||||
|
async ({ event }) => { |
||||
|
const data = await readValidatedBody( |
||||
|
event, |
||||
|
validateZod(UserConfigUpdateSchema, event) |
||||
|
); |
||||
|
await Database.userConfigs.update('wg0', data); |
||||
|
await WireGuard.saveConfig(); |
||||
|
return { success: true }; |
||||
|
} |
||||
|
); |
@ -1,9 +0,0 @@ |
|||||
export default defineEventHandler(async (event) => { |
|
||||
const data = await readValidatedBody( |
|
||||
event, |
|
||||
validateZod(cidrUpdateType, event) |
|
||||
); |
|
||||
|
|
||||
await WireGuard.updateAddressRange(data); |
|
||||
return { success: true }; |
|
||||
}); |
|
@ -1,4 +0,0 @@ |
|||||
export default defineEventHandler(async () => { |
|
||||
const system = await Database.system.get(); |
|
||||
return system.userConfig; |
|
||||
}); |
|
@ -1,9 +0,0 @@ |
|||||
export default defineEventHandler(async (event) => { |
|
||||
const data = await readValidatedBody( |
|
||||
event, |
|
||||
validateZod(userConfigUpdateType, event) |
|
||||
); |
|
||||
await Database.system.updateUserConfig(data); |
|
||||
await WireGuard.saveConfig(); |
|
||||
return { success: true }; |
|
||||
}); |
|
@ -1,20 +1,31 @@ |
|||||
export default defineEventHandler(async (event) => { |
import { ClientGetSchema } from '#db/repositories/client/types'; |
||||
const { clientId } = await getValidatedRouterParams( |
|
||||
event, |
export default definePermissionEventHandler( |
||||
validateZod(clientIdType) |
actions.CLIENT, |
||||
); |
async ({ event }) => { |
||||
const client = await WireGuard.getClient({ clientId }); |
const { clientId } = await getValidatedRouterParams( |
||||
const config = await WireGuard.getClientConfiguration({ clientId }); |
event, |
||||
const configName = client.name |
validateZod(ClientGetSchema) |
||||
.replace(/[^a-zA-Z0-9_=+.-]/g, '-') |
); |
||||
.replace(/(-{2,}|-$)/g, '-') |
const client = await Database.clients.get(clientId); |
||||
.replace(/-$/, '') |
if (!client) { |
||||
.substring(0, 32); |
throw createError({ |
||||
setHeader( |
statusCode: 404, |
||||
event, |
statusMessage: 'Client not found', |
||||
'Content-Disposition', |
}); |
||||
`attachment; filename="${configName || clientId}.conf"` |
} |
||||
); |
const config = await WireGuard.getClientConfiguration({ clientId }); |
||||
setHeader(event, 'Content-Type', 'text/plain'); |
const configName = client.name |
||||
return config; |
.replace(/[^a-zA-Z0-9_=+.-]/g, '-') |
||||
}); |
.replace(/(-{2,}|-$)/g, '-') |
||||
|
.replace(/-$/, '') |
||||
|
.substring(0, 32); |
||||
|
setHeader( |
||||
|
event, |
||||
|
'Content-Disposition', |
||||
|
`attachment; filename="${configName || clientId}.conf"` |
||||
|
); |
||||
|
setHeader(event, 'Content-Type', 'text/plain'); |
||||
|
return config; |
||||
|
} |
||||
|
); |
||||
|
@ -1,8 +1,14 @@ |
|||||
export default defineEventHandler(async (event) => { |
import { ClientGetSchema } from '#db/repositories/client/types'; |
||||
const { clientId } = await getValidatedRouterParams( |
|
||||
event, |
export default definePermissionEventHandler( |
||||
validateZod(clientIdType) |
actions.CLIENT, |
||||
); |
async ({ event }) => { |
||||
await WireGuard.disableClient({ clientId }); |
const { clientId } = await getValidatedRouterParams( |
||||
return { success: true }; |
event, |
||||
}); |
validateZod(ClientGetSchema) |
||||
|
); |
||||
|
await Database.clients.toggle(clientId, false); |
||||
|
await WireGuard.saveConfig(); |
||||
|
return { success: true }; |
||||
|
} |
||||
|
); |
||||
|
@ -1,8 +1,14 @@ |
|||||
export default defineEventHandler(async (event) => { |
import { ClientGetSchema } from '#db/repositories/client/types'; |
||||
const { clientId } = await getValidatedRouterParams( |
|
||||
event, |
export default definePermissionEventHandler( |
||||
validateZod(clientIdType) |
actions.CLIENT, |
||||
); |
async ({ event }) => { |
||||
await WireGuard.enableClient({ clientId }); |
const { clientId } = await getValidatedRouterParams( |
||||
return { success: true }; |
event, |
||||
}); |
validateZod(ClientGetSchema) |
||||
|
); |
||||
|
await Database.clients.toggle(clientId, false); |
||||
|
await WireGuard.saveConfig(); |
||||
|
return { success: true }; |
||||
|
} |
||||
|
); |
||||
|
@ -1,8 +1,13 @@ |
|||||
export default defineEventHandler(async (event) => { |
import { ClientGetSchema } from '#db/repositories/client/types'; |
||||
const { clientId } = await getValidatedRouterParams( |
|
||||
event, |
export default definePermissionEventHandler( |
||||
validateZod(clientIdType) |
actions.CLIENT, |
||||
); |
async ({ event }) => { |
||||
await WireGuard.generateOneTimeLink({ clientId }); |
const { clientId } = await getValidatedRouterParams( |
||||
return { success: true }; |
event, |
||||
}); |
validateZod(ClientGetSchema) |
||||
|
); |
||||
|
await Database.oneTimeLinks.generate(clientId); |
||||
|
return { success: true }; |
||||
|
} |
||||
|
); |
||||
|
@ -1,8 +1,14 @@ |
|||||
export default defineEventHandler(async (event) => { |
import { ClientGetSchema } from '#db/repositories/client/types'; |
||||
const { clientId } = await getValidatedRouterParams( |
|
||||
event, |
export default definePermissionEventHandler( |
||||
validateZod(clientIdType) |
actions.CLIENT, |
||||
); |
async ({ event }) => { |
||||
await WireGuard.deleteClient({ clientId }); |
const { clientId } = await getValidatedRouterParams( |
||||
return { success: true }; |
event, |
||||
}); |
validateZod(ClientGetSchema) |
||||
|
); |
||||
|
await Database.clients.delete(clientId); |
||||
|
await WireGuard.saveConfig(); |
||||
|
return { success: true }; |
||||
|
} |
||||
|
); |
||||
|
@ -1,7 +1,19 @@ |
|||||
export default defineEventHandler(async (event) => { |
import { ClientGetSchema } from '~~/server/database/repositories/client/types'; |
||||
const { clientId } = await getValidatedRouterParams( |
|
||||
event, |
export default definePermissionEventHandler( |
||||
validateZod(clientIdType) |
actions.CLIENT, |
||||
); |
async ({ event }) => { |
||||
return WireGuard.getClient({ clientId }); |
const { clientId } = await getValidatedRouterParams( |
||||
}); |
event, |
||||
|
validateZod(ClientGetSchema, event) |
||||
|
); |
||||
|
const result = await Database.clients.get(clientId); |
||||
|
if (!result) { |
||||
|
throw createError({ |
||||
|
statusCode: 404, |
||||
|
statusMessage: 'Client not found', |
||||
|
}); |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
); |
||||
|
@ -1,18 +1,24 @@ |
|||||
export default defineEventHandler(async (event) => { |
import { |
||||
const { clientId } = await getValidatedRouterParams( |
ClientGetSchema, |
||||
event, |
ClientUpdateSchema, |
||||
validateZod(clientIdType) |
} from '#db/repositories/client/types'; |
||||
); |
|
||||
|
|
||||
const data = await readValidatedBody( |
export default definePermissionEventHandler( |
||||
event, |
actions.CLIENT, |
||||
validateZod(clientUpdateType, event) |
async ({ event }) => { |
||||
); |
const { clientId } = await getValidatedRouterParams( |
||||
|
event, |
||||
|
validateZod(ClientGetSchema) |
||||
|
); |
||||
|
|
||||
await WireGuard.updateClient({ |
const data = await readValidatedBody( |
||||
clientId, |
event, |
||||
client: data, |
validateZod(ClientUpdateSchema, event) |
||||
}); |
); |
||||
|
|
||||
return { success: true }; |
await Database.clients.update(clientId, data); |
||||
}); |
await WireGuard.saveConfig(); |
||||
|
|
||||
|
return { success: true }; |
||||
|
} |
||||
|
); |
||||
|
@ -1,9 +1,14 @@ |
|||||
export default defineEventHandler(async (event) => { |
import { ClientGetSchema } from '#db/repositories/client/types'; |
||||
const { clientId } = await getValidatedRouterParams( |
|
||||
event, |
export default definePermissionEventHandler( |
||||
validateZod(clientIdType) |
actions.CLIENT, |
||||
); |
async ({ event }) => { |
||||
const svg = await WireGuard.getClientQRCodeSVG({ clientId }); |
const { clientId } = await getValidatedRouterParams( |
||||
setHeader(event, 'Content-Type', 'image/svg+xml'); |
event, |
||||
return svg; |
validateZod(ClientGetSchema) |
||||
}); |
); |
||||
|
const svg = await WireGuard.getClientQRCodeSVG({ clientId }); |
||||
|
setHeader(event, 'Content-Type', 'image/svg+xml'); |
||||
|
return svg; |
||||
|
} |
||||
|
); |
||||
|
@ -1,3 +1,3 @@ |
|||||
export default defineEventHandler(() => { |
export default definePermissionEventHandler(actions.CLIENT, () => { |
||||
return WireGuard.getClients(); |
return WireGuard.getClients(); |
||||
}); |
}); |
||||
|
@ -1,8 +1,14 @@ |
|||||
export default defineEventHandler(async (event) => { |
import { ClientCreateSchema } from '#db/repositories/client/types'; |
||||
const { name, expireDate } = await readValidatedBody( |
|
||||
event, |
export default definePermissionEventHandler( |
||||
validateZod(createType) |
actions.CLIENT, |
||||
); |
async ({ event }) => { |
||||
await WireGuard.createClient({ name, expireDate }); |
const { name, expiresAt } = await readValidatedBody( |
||||
return { success: true }; |
event, |
||||
}); |
validateZod(ClientCreateSchema) |
||||
|
); |
||||
|
await Database.clients.create({ name, expiresAt }); |
||||
|
await WireGuard.saveConfig(); |
||||
|
return { success: true }; |
||||
|
} |
||||
|
); |
||||
|
@ -1,17 +1,12 @@ |
|||||
export default defineEventHandler(async (event) => { |
import { UserSetupType } from '#db/repositories/user/types'; |
||||
const setupDone = await Database.setup.done(); |
|
||||
if (setupDone) { |
|
||||
throw createError({ |
|
||||
statusCode: 400, |
|
||||
statusMessage: 'Invalid state', |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
|
export default defineSetupEventHandler(async ({ event }) => { |
||||
const { username, password } = await readValidatedBody( |
const { username, password } = await readValidatedBody( |
||||
event, |
event, |
||||
validateZod(passwordSetupType, event) |
validateZod(UserSetupType, event) |
||||
); |
); |
||||
await Database.user.create(username, password); |
|
||||
await Database.setup.set(5); |
await Database.users.create(username, password); |
||||
|
await Database.general.setSetupStep(5); |
||||
return { success: true }; |
return { success: true }; |
||||
}); |
}); |
||||
|
@ -1,17 +1,11 @@ |
|||||
export default defineEventHandler(async (event) => { |
import { UserConfigSetupType } from '#db/repositories/userConfig/types'; |
||||
const setupDone = await Database.setup.done(); |
|
||||
if (setupDone) { |
|
||||
throw createError({ |
|
||||
statusCode: 400, |
|
||||
statusMessage: 'Invalid state', |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
|
export default defineSetupEventHandler(async ({ event }) => { |
||||
const { host, port } = await readValidatedBody( |
const { host, port } = await readValidatedBody( |
||||
event, |
event, |
||||
validateZod(hostPortType, event) |
validateZod(UserConfigSetupType, event) |
||||
); |
); |
||||
await Database.system.updateClientsHostPort(host, port); |
await Database.userConfigs.updateHostPort('wg0', host, port); |
||||
await Database.setup.set('success'); |
await Database.general.setSetupStep(0); |
||||
return { success: true }; |
return { success: true }; |
||||
}); |
}); |
||||
|
@ -1,6 +1,9 @@ |
|||||
export default defineEventHandler(async (event) => { |
export default definePermissionEventHandler( |
||||
const config = await WireGuard.backupConfiguration(); |
actions.ADMIN, |
||||
setHeader(event, 'Content-Disposition', 'attachment; filename="wg0.json"'); |
async (/*{ event }*/) => { |
||||
setHeader(event, 'Content-Type', 'text/json'); |
/*const config = await WireGuard.backupConfiguration(); |
||||
return config; |
setHeader(event, 'Content-Disposition', 'attachment; filename="wg0.json"'); |
||||
}); |
setHeader(event, 'Content-Type', 'text/json'); |
||||
|
return config;*/ |
||||
|
} |
||||
|
); |
||||
|
@ -1,5 +1,8 @@ |
|||||
export default defineEventHandler(async (event) => { |
export default definePermissionEventHandler( |
||||
const { file } = await readValidatedBody(event, validateZod(fileType)); |
actions.ADMIN, |
||||
await WireGuard.restoreConfiguration(file); |
async (/*{ event }*/) => { |
||||
return { success: true }; |
/*const { file } = await readValidatedBody(event, validateZod(fileType)); |
||||
}); |
await WireGuard.restoreConfiguration(file); |
||||
|
return { success: true };*/ |
||||
|
} |
||||
|
); |
||||
|
@ -0,0 +1,100 @@ |
|||||
|
CREATE TABLE `clients_table` ( |
||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, |
||||
|
`name` text NOT NULL, |
||||
|
`ipv4_address` text NOT NULL, |
||||
|
`ipv6_address` text NOT NULL, |
||||
|
`private_key` text NOT NULL, |
||||
|
`public_key` text NOT NULL, |
||||
|
`pre_shared_key` text NOT NULL, |
||||
|
`expires_at` text, |
||||
|
`allowed_ips` text NOT NULL, |
||||
|
`server_allowed_ips` text NOT NULL, |
||||
|
`persistent_keepalive` integer NOT NULL, |
||||
|
`mtu` integer NOT NULL, |
||||
|
`dns` text NOT NULL, |
||||
|
`enabled` integer NOT NULL, |
||||
|
`created_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL, |
||||
|
`updated_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL |
||||
|
); |
||||
|
--> statement-breakpoint |
||||
|
CREATE UNIQUE INDEX `clients_table_ipv4_address_unique` ON `clients_table` (`ipv4_address`);--> statement-breakpoint |
||||
|
CREATE UNIQUE INDEX `clients_table_ipv6_address_unique` ON `clients_table` (`ipv6_address`);--> statement-breakpoint |
||||
|
CREATE TABLE `general_table` ( |
||||
|
`id` integer PRIMARY KEY DEFAULT 1 NOT NULL, |
||||
|
`setupStep` integer NOT NULL, |
||||
|
`session_password` text NOT NULL, |
||||
|
`session_timeout` integer NOT NULL, |
||||
|
`created_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL, |
||||
|
`updated_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL |
||||
|
); |
||||
|
--> statement-breakpoint |
||||
|
CREATE TABLE `hooks_table` ( |
||||
|
`id` text PRIMARY KEY NOT NULL, |
||||
|
`pre_up` text NOT NULL, |
||||
|
`post_up` text NOT NULL, |
||||
|
`pre_down` text NOT NULL, |
||||
|
`post_down` 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 `interfaces_table` ( |
||||
|
`name` text PRIMARY KEY NOT NULL, |
||||
|
`device` text NOT NULL, |
||||
|
`port` integer NOT NULL, |
||||
|
`private_key` text NOT NULL, |
||||
|
`public_key` text NOT NULL, |
||||
|
`ipv4_cidr` text NOT NULL, |
||||
|
`ipv6_cidr` text NOT NULL, |
||||
|
`mtu` integer NOT NULL, |
||||
|
`enabled` integer NOT NULL, |
||||
|
`created_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL, |
||||
|
`updated_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL |
||||
|
); |
||||
|
--> 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, |
||||
|
`expires_at` text NOT NULL, |
||||
|
`clientId` integer NOT NULL, |
||||
|
`created_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL, |
||||
|
`updated_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL, |
||||
|
FOREIGN KEY (`clientId`) REFERENCES `clients_table`(`id`) ON UPDATE cascade ON DELETE cascade |
||||
|
); |
||||
|
--> statement-breakpoint |
||||
|
CREATE UNIQUE INDEX `one_time_links_table_one_time_link_unique` ON `one_time_links_table` (`one_time_link`);--> statement-breakpoint |
||||
|
CREATE TABLE `users_table` ( |
||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, |
||||
|
`username` text NOT NULL, |
||||
|
`password` text NOT NULL, |
||||
|
`email` text, |
||||
|
`name` text NOT NULL, |
||||
|
`role` integer NOT NULL, |
||||
|
`enabled` integer NOT NULL, |
||||
|
`created_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL, |
||||
|
`updated_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL |
||||
|
); |
||||
|
--> statement-breakpoint |
||||
|
CREATE UNIQUE INDEX `users_table_username_unique` ON `users_table` (`username`);--> statement-breakpoint |
||||
|
CREATE TABLE `user_configs_table` ( |
||||
|
`id` text PRIMARY KEY NOT NULL, |
||||
|
`default_mtu` integer NOT NULL, |
||||
|
`default_persistent_keepalive` integer NOT NULL, |
||||
|
`default_dns` text NOT NULL, |
||||
|
`default_allowed_ips` text NOT NULL, |
||||
|
`host` text NOT NULL, |
||||
|
`port` integer 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 |
||||
|
); |
@ -0,0 +1,18 @@ |
|||||
|
PRAGMA journal_mode=WAL;--> statement-breakpoint |
||||
|
INSERT INTO `general_table` (`setupStep`, `session_password`, `session_timeout`) |
||||
|
VALUES (1, hex(randomblob(256)), 3600); |
||||
|
--> 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); |
||||
|
--> statement-breakpoint |
||||
|
INSERT INTO `hooks_table` (`id`, `pre_up`, `post_up`, `pre_down`, `post_down`) |
||||
|
VALUES ( |
||||
|
'wg0', |
||||
|
'', |
||||
|
'iptables -t nat -A POSTROUTING -s {{ipv4Cidr}} -o {{device}} -j MASQUERADE; iptables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; ip6tables -t nat -A POSTROUTING -s {{ipv6Cidr}} -o {{device}} -j MASQUERADE; ip6tables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -A FORWARD -o wg0 -j ACCEPT;', |
||||
|
'', |
||||
|
'iptables -t nat -D POSTROUTING -s {{ipv4Cidr}} -o {{device}} -j MASQUERADE; iptables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; ip6tables -t nat -D POSTROUTING -s {{ipv6Cidr}} -o {{device}} -j MASQUERADE; ip6tables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -D FORWARD -o wg0 -j ACCEPT;' |
||||
|
); |
||||
|
--> statement-breakpoint |
||||
|
INSERT INTO `user_configs_table` (`id`, `default_mtu`, `default_persistent_keepalive`, `default_dns`, `default_allowed_ips`, `host`, `port`) |
||||
|
VALUES ('wg0', 1420, 0, '["1.1.1.1","2606:4700:4700::1111"]', '["0.0.0.0/0","::/0"]', '', 51820) |
@ -0,0 +1,686 @@ |
|||||
|
{ |
||||
|
"version": "6", |
||||
|
"dialect": "sqlite", |
||||
|
"id": "25907c5f-be21-4ae6-88c4-1a72b2f335e7", |
||||
|
"prevId": "00000000-0000-0000-0000-000000000000", |
||||
|
"tables": { |
||||
|
"clients_table": { |
||||
|
"name": "clients_table", |
||||
|
"columns": { |
||||
|
"id": { |
||||
|
"name": "id", |
||||
|
"type": "integer", |
||||
|
"primaryKey": true, |
||||
|
"notNull": true, |
||||
|
"autoincrement": true |
||||
|
}, |
||||
|
"name": { |
||||
|
"name": "name", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"ipv4_address": { |
||||
|
"name": "ipv4_address", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"ipv6_address": { |
||||
|
"name": "ipv6_address", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"private_key": { |
||||
|
"name": "private_key", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"public_key": { |
||||
|
"name": "public_key", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"pre_shared_key": { |
||||
|
"name": "pre_shared_key", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"expires_at": { |
||||
|
"name": "expires_at", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": false, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"allowed_ips": { |
||||
|
"name": "allowed_ips", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"server_allowed_ips": { |
||||
|
"name": "server_allowed_ips", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"persistent_keepalive": { |
||||
|
"name": "persistent_keepalive", |
||||
|
"type": "integer", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"mtu": { |
||||
|
"name": "mtu", |
||||
|
"type": "integer", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"dns": { |
||||
|
"name": "dns", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"enabled": { |
||||
|
"name": "enabled", |
||||
|
"type": "integer", |
||||
|
"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": { |
||||
|
"clients_table_ipv4_address_unique": { |
||||
|
"name": "clients_table_ipv4_address_unique", |
||||
|
"columns": [ |
||||
|
"ipv4_address" |
||||
|
], |
||||
|
"isUnique": true |
||||
|
}, |
||||
|
"clients_table_ipv6_address_unique": { |
||||
|
"name": "clients_table_ipv6_address_unique", |
||||
|
"columns": [ |
||||
|
"ipv6_address" |
||||
|
], |
||||
|
"isUnique": true |
||||
|
} |
||||
|
}, |
||||
|
"foreignKeys": {}, |
||||
|
"compositePrimaryKeys": {}, |
||||
|
"uniqueConstraints": {}, |
||||
|
"checkConstraints": {} |
||||
|
}, |
||||
|
"general_table": { |
||||
|
"name": "general_table", |
||||
|
"columns": { |
||||
|
"id": { |
||||
|
"name": "id", |
||||
|
"type": "integer", |
||||
|
"primaryKey": true, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false, |
||||
|
"default": 1 |
||||
|
}, |
||||
|
"setupStep": { |
||||
|
"name": "setupStep", |
||||
|
"type": "integer", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"session_password": { |
||||
|
"name": "session_password", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"session_timeout": { |
||||
|
"name": "session_timeout", |
||||
|
"type": "integer", |
||||
|
"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": {}, |
||||
|
"compositePrimaryKeys": {}, |
||||
|
"uniqueConstraints": {}, |
||||
|
"checkConstraints": {} |
||||
|
}, |
||||
|
"hooks_table": { |
||||
|
"name": "hooks_table", |
||||
|
"columns": { |
||||
|
"id": { |
||||
|
"name": "id", |
||||
|
"type": "text", |
||||
|
"primaryKey": true, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"pre_up": { |
||||
|
"name": "pre_up", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"post_up": { |
||||
|
"name": "post_up", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"pre_down": { |
||||
|
"name": "pre_down", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"post_down": { |
||||
|
"name": "post_down", |
||||
|
"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": { |
||||
|
"hooks_table_id_interfaces_table_name_fk": { |
||||
|
"name": "hooks_table_id_interfaces_table_name_fk", |
||||
|
"tableFrom": "hooks_table", |
||||
|
"tableTo": "interfaces_table", |
||||
|
"columnsFrom": [ |
||||
|
"id" |
||||
|
], |
||||
|
"columnsTo": [ |
||||
|
"name" |
||||
|
], |
||||
|
"onDelete": "cascade", |
||||
|
"onUpdate": "cascade" |
||||
|
} |
||||
|
}, |
||||
|
"compositePrimaryKeys": {}, |
||||
|
"uniqueConstraints": {}, |
||||
|
"checkConstraints": {} |
||||
|
}, |
||||
|
"interfaces_table": { |
||||
|
"name": "interfaces_table", |
||||
|
"columns": { |
||||
|
"name": { |
||||
|
"name": "name", |
||||
|
"type": "text", |
||||
|
"primaryKey": true, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"device": { |
||||
|
"name": "device", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"port": { |
||||
|
"name": "port", |
||||
|
"type": "integer", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"private_key": { |
||||
|
"name": "private_key", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"public_key": { |
||||
|
"name": "public_key", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"ipv4_cidr": { |
||||
|
"name": "ipv4_cidr", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"ipv6_cidr": { |
||||
|
"name": "ipv6_cidr", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"mtu": { |
||||
|
"name": "mtu", |
||||
|
"type": "integer", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"enabled": { |
||||
|
"name": "enabled", |
||||
|
"type": "integer", |
||||
|
"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": { |
||||
|
"interfaces_table_port_unique": { |
||||
|
"name": "interfaces_table_port_unique", |
||||
|
"columns": [ |
||||
|
"port" |
||||
|
], |
||||
|
"isUnique": true |
||||
|
} |
||||
|
}, |
||||
|
"foreignKeys": {}, |
||||
|
"compositePrimaryKeys": {}, |
||||
|
"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": { |
||||
|
"id": { |
||||
|
"name": "id", |
||||
|
"type": "integer", |
||||
|
"primaryKey": true, |
||||
|
"notNull": true, |
||||
|
"autoincrement": true |
||||
|
}, |
||||
|
"one_time_link": { |
||||
|
"name": "one_time_link", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"expires_at": { |
||||
|
"name": "expires_at", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"clientId": { |
||||
|
"name": "clientId", |
||||
|
"type": "integer", |
||||
|
"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": { |
||||
|
"one_time_links_table_one_time_link_unique": { |
||||
|
"name": "one_time_links_table_one_time_link_unique", |
||||
|
"columns": [ |
||||
|
"one_time_link" |
||||
|
], |
||||
|
"isUnique": true |
||||
|
} |
||||
|
}, |
||||
|
"foreignKeys": { |
||||
|
"one_time_links_table_clientId_clients_table_id_fk": { |
||||
|
"name": "one_time_links_table_clientId_clients_table_id_fk", |
||||
|
"tableFrom": "one_time_links_table", |
||||
|
"tableTo": "clients_table", |
||||
|
"columnsFrom": [ |
||||
|
"clientId" |
||||
|
], |
||||
|
"columnsTo": [ |
||||
|
"id" |
||||
|
], |
||||
|
"onDelete": "cascade", |
||||
|
"onUpdate": "cascade" |
||||
|
} |
||||
|
}, |
||||
|
"compositePrimaryKeys": {}, |
||||
|
"uniqueConstraints": {}, |
||||
|
"checkConstraints": {} |
||||
|
}, |
||||
|
"users_table": { |
||||
|
"name": "users_table", |
||||
|
"columns": { |
||||
|
"id": { |
||||
|
"name": "id", |
||||
|
"type": "integer", |
||||
|
"primaryKey": true, |
||||
|
"notNull": true, |
||||
|
"autoincrement": true |
||||
|
}, |
||||
|
"username": { |
||||
|
"name": "username", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"password": { |
||||
|
"name": "password", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"email": { |
||||
|
"name": "email", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": false, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"name": { |
||||
|
"name": "name", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"role": { |
||||
|
"name": "role", |
||||
|
"type": "integer", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"enabled": { |
||||
|
"name": "enabled", |
||||
|
"type": "integer", |
||||
|
"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": { |
||||
|
"users_table_username_unique": { |
||||
|
"name": "users_table_username_unique", |
||||
|
"columns": [ |
||||
|
"username" |
||||
|
], |
||||
|
"isUnique": true |
||||
|
} |
||||
|
}, |
||||
|
"foreignKeys": {}, |
||||
|
"compositePrimaryKeys": {}, |
||||
|
"uniqueConstraints": {}, |
||||
|
"checkConstraints": {} |
||||
|
}, |
||||
|
"user_configs_table": { |
||||
|
"name": "user_configs_table", |
||||
|
"columns": { |
||||
|
"id": { |
||||
|
"name": "id", |
||||
|
"type": "text", |
||||
|
"primaryKey": true, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"default_mtu": { |
||||
|
"name": "default_mtu", |
||||
|
"type": "integer", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"default_persistent_keepalive": { |
||||
|
"name": "default_persistent_keepalive", |
||||
|
"type": "integer", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"default_dns": { |
||||
|
"name": "default_dns", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"default_allowed_ips": { |
||||
|
"name": "default_allowed_ips", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"host": { |
||||
|
"name": "host", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"port": { |
||||
|
"name": "port", |
||||
|
"type": "integer", |
||||
|
"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": { |
||||
|
"user_configs_table_id_interfaces_table_name_fk": { |
||||
|
"name": "user_configs_table_id_interfaces_table_name_fk", |
||||
|
"tableFrom": "user_configs_table", |
||||
|
"tableTo": "interfaces_table", |
||||
|
"columnsFrom": [ |
||||
|
"id" |
||||
|
], |
||||
|
"columnsTo": [ |
||||
|
"name" |
||||
|
], |
||||
|
"onDelete": "cascade", |
||||
|
"onUpdate": "cascade" |
||||
|
} |
||||
|
}, |
||||
|
"compositePrimaryKeys": {}, |
||||
|
"uniqueConstraints": {}, |
||||
|
"checkConstraints": {} |
||||
|
} |
||||
|
}, |
||||
|
"views": {}, |
||||
|
"enums": {}, |
||||
|
"_meta": { |
||||
|
"schemas": {}, |
||||
|
"tables": {}, |
||||
|
"columns": {} |
||||
|
}, |
||||
|
"internal": { |
||||
|
"indexes": {} |
||||
|
} |
||||
|
} |
@ -0,0 +1,686 @@ |
|||||
|
{ |
||||
|
"id": "60af732f-adc0-405d-96cc-2f818585f593", |
||||
|
"prevId": "25907c5f-be21-4ae6-88c4-1a72b2f335e7", |
||||
|
"version": "6", |
||||
|
"dialect": "sqlite", |
||||
|
"tables": { |
||||
|
"clients_table": { |
||||
|
"name": "clients_table", |
||||
|
"columns": { |
||||
|
"id": { |
||||
|
"name": "id", |
||||
|
"type": "integer", |
||||
|
"primaryKey": true, |
||||
|
"notNull": true, |
||||
|
"autoincrement": true |
||||
|
}, |
||||
|
"name": { |
||||
|
"name": "name", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"ipv4_address": { |
||||
|
"name": "ipv4_address", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"ipv6_address": { |
||||
|
"name": "ipv6_address", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"private_key": { |
||||
|
"name": "private_key", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"public_key": { |
||||
|
"name": "public_key", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"pre_shared_key": { |
||||
|
"name": "pre_shared_key", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"expires_at": { |
||||
|
"name": "expires_at", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": false, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"allowed_ips": { |
||||
|
"name": "allowed_ips", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"server_allowed_ips": { |
||||
|
"name": "server_allowed_ips", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"persistent_keepalive": { |
||||
|
"name": "persistent_keepalive", |
||||
|
"type": "integer", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"mtu": { |
||||
|
"name": "mtu", |
||||
|
"type": "integer", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"dns": { |
||||
|
"name": "dns", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"enabled": { |
||||
|
"name": "enabled", |
||||
|
"type": "integer", |
||||
|
"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": { |
||||
|
"clients_table_ipv4_address_unique": { |
||||
|
"name": "clients_table_ipv4_address_unique", |
||||
|
"columns": [ |
||||
|
"ipv4_address" |
||||
|
], |
||||
|
"isUnique": true |
||||
|
}, |
||||
|
"clients_table_ipv6_address_unique": { |
||||
|
"name": "clients_table_ipv6_address_unique", |
||||
|
"columns": [ |
||||
|
"ipv6_address" |
||||
|
], |
||||
|
"isUnique": true |
||||
|
} |
||||
|
}, |
||||
|
"foreignKeys": {}, |
||||
|
"compositePrimaryKeys": {}, |
||||
|
"uniqueConstraints": {}, |
||||
|
"checkConstraints": {} |
||||
|
}, |
||||
|
"general_table": { |
||||
|
"name": "general_table", |
||||
|
"columns": { |
||||
|
"id": { |
||||
|
"name": "id", |
||||
|
"type": "integer", |
||||
|
"primaryKey": true, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false, |
||||
|
"default": 1 |
||||
|
}, |
||||
|
"setupStep": { |
||||
|
"name": "setupStep", |
||||
|
"type": "integer", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"session_password": { |
||||
|
"name": "session_password", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"session_timeout": { |
||||
|
"name": "session_timeout", |
||||
|
"type": "integer", |
||||
|
"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": {}, |
||||
|
"compositePrimaryKeys": {}, |
||||
|
"uniqueConstraints": {}, |
||||
|
"checkConstraints": {} |
||||
|
}, |
||||
|
"hooks_table": { |
||||
|
"name": "hooks_table", |
||||
|
"columns": { |
||||
|
"id": { |
||||
|
"name": "id", |
||||
|
"type": "text", |
||||
|
"primaryKey": true, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"pre_up": { |
||||
|
"name": "pre_up", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"post_up": { |
||||
|
"name": "post_up", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"pre_down": { |
||||
|
"name": "pre_down", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"post_down": { |
||||
|
"name": "post_down", |
||||
|
"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": { |
||||
|
"hooks_table_id_interfaces_table_name_fk": { |
||||
|
"name": "hooks_table_id_interfaces_table_name_fk", |
||||
|
"tableFrom": "hooks_table", |
||||
|
"columnsFrom": [ |
||||
|
"id" |
||||
|
], |
||||
|
"tableTo": "interfaces_table", |
||||
|
"columnsTo": [ |
||||
|
"name" |
||||
|
], |
||||
|
"onUpdate": "cascade", |
||||
|
"onDelete": "cascade" |
||||
|
} |
||||
|
}, |
||||
|
"compositePrimaryKeys": {}, |
||||
|
"uniqueConstraints": {}, |
||||
|
"checkConstraints": {} |
||||
|
}, |
||||
|
"interfaces_table": { |
||||
|
"name": "interfaces_table", |
||||
|
"columns": { |
||||
|
"name": { |
||||
|
"name": "name", |
||||
|
"type": "text", |
||||
|
"primaryKey": true, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"device": { |
||||
|
"name": "device", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"port": { |
||||
|
"name": "port", |
||||
|
"type": "integer", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"private_key": { |
||||
|
"name": "private_key", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"public_key": { |
||||
|
"name": "public_key", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"ipv4_cidr": { |
||||
|
"name": "ipv4_cidr", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"ipv6_cidr": { |
||||
|
"name": "ipv6_cidr", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"mtu": { |
||||
|
"name": "mtu", |
||||
|
"type": "integer", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"enabled": { |
||||
|
"name": "enabled", |
||||
|
"type": "integer", |
||||
|
"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": { |
||||
|
"interfaces_table_port_unique": { |
||||
|
"name": "interfaces_table_port_unique", |
||||
|
"columns": [ |
||||
|
"port" |
||||
|
], |
||||
|
"isUnique": true |
||||
|
} |
||||
|
}, |
||||
|
"foreignKeys": {}, |
||||
|
"compositePrimaryKeys": {}, |
||||
|
"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": { |
||||
|
"id": { |
||||
|
"name": "id", |
||||
|
"type": "integer", |
||||
|
"primaryKey": true, |
||||
|
"notNull": true, |
||||
|
"autoincrement": true |
||||
|
}, |
||||
|
"one_time_link": { |
||||
|
"name": "one_time_link", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"expires_at": { |
||||
|
"name": "expires_at", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"clientId": { |
||||
|
"name": "clientId", |
||||
|
"type": "integer", |
||||
|
"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": { |
||||
|
"one_time_links_table_one_time_link_unique": { |
||||
|
"name": "one_time_links_table_one_time_link_unique", |
||||
|
"columns": [ |
||||
|
"one_time_link" |
||||
|
], |
||||
|
"isUnique": true |
||||
|
} |
||||
|
}, |
||||
|
"foreignKeys": { |
||||
|
"one_time_links_table_clientId_clients_table_id_fk": { |
||||
|
"name": "one_time_links_table_clientId_clients_table_id_fk", |
||||
|
"tableFrom": "one_time_links_table", |
||||
|
"columnsFrom": [ |
||||
|
"clientId" |
||||
|
], |
||||
|
"tableTo": "clients_table", |
||||
|
"columnsTo": [ |
||||
|
"id" |
||||
|
], |
||||
|
"onUpdate": "cascade", |
||||
|
"onDelete": "cascade" |
||||
|
} |
||||
|
}, |
||||
|
"compositePrimaryKeys": {}, |
||||
|
"uniqueConstraints": {}, |
||||
|
"checkConstraints": {} |
||||
|
}, |
||||
|
"users_table": { |
||||
|
"name": "users_table", |
||||
|
"columns": { |
||||
|
"id": { |
||||
|
"name": "id", |
||||
|
"type": "integer", |
||||
|
"primaryKey": true, |
||||
|
"notNull": true, |
||||
|
"autoincrement": true |
||||
|
}, |
||||
|
"username": { |
||||
|
"name": "username", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"password": { |
||||
|
"name": "password", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"email": { |
||||
|
"name": "email", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": false, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"name": { |
||||
|
"name": "name", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"role": { |
||||
|
"name": "role", |
||||
|
"type": "integer", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"enabled": { |
||||
|
"name": "enabled", |
||||
|
"type": "integer", |
||||
|
"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": { |
||||
|
"users_table_username_unique": { |
||||
|
"name": "users_table_username_unique", |
||||
|
"columns": [ |
||||
|
"username" |
||||
|
], |
||||
|
"isUnique": true |
||||
|
} |
||||
|
}, |
||||
|
"foreignKeys": {}, |
||||
|
"compositePrimaryKeys": {}, |
||||
|
"uniqueConstraints": {}, |
||||
|
"checkConstraints": {} |
||||
|
}, |
||||
|
"user_configs_table": { |
||||
|
"name": "user_configs_table", |
||||
|
"columns": { |
||||
|
"id": { |
||||
|
"name": "id", |
||||
|
"type": "text", |
||||
|
"primaryKey": true, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"default_mtu": { |
||||
|
"name": "default_mtu", |
||||
|
"type": "integer", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"default_persistent_keepalive": { |
||||
|
"name": "default_persistent_keepalive", |
||||
|
"type": "integer", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"default_dns": { |
||||
|
"name": "default_dns", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"default_allowed_ips": { |
||||
|
"name": "default_allowed_ips", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"host": { |
||||
|
"name": "host", |
||||
|
"type": "text", |
||||
|
"primaryKey": false, |
||||
|
"notNull": true, |
||||
|
"autoincrement": false |
||||
|
}, |
||||
|
"port": { |
||||
|
"name": "port", |
||||
|
"type": "integer", |
||||
|
"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": { |
||||
|
"user_configs_table_id_interfaces_table_name_fk": { |
||||
|
"name": "user_configs_table_id_interfaces_table_name_fk", |
||||
|
"tableFrom": "user_configs_table", |
||||
|
"columnsFrom": [ |
||||
|
"id" |
||||
|
], |
||||
|
"tableTo": "interfaces_table", |
||||
|
"columnsTo": [ |
||||
|
"name" |
||||
|
], |
||||
|
"onUpdate": "cascade", |
||||
|
"onDelete": "cascade" |
||||
|
} |
||||
|
}, |
||||
|
"compositePrimaryKeys": {}, |
||||
|
"uniqueConstraints": {}, |
||||
|
"checkConstraints": {} |
||||
|
} |
||||
|
}, |
||||
|
"views": {}, |
||||
|
"enums": {}, |
||||
|
"_meta": { |
||||
|
"columns": {}, |
||||
|
"schemas": {}, |
||||
|
"tables": {} |
||||
|
}, |
||||
|
"internal": { |
||||
|
"indexes": {} |
||||
|
} |
||||
|
} |
@ -0,0 +1,20 @@ |
|||||
|
{ |
||||
|
"version": "7", |
||||
|
"dialect": "sqlite", |
||||
|
"entries": [ |
||||
|
{ |
||||
|
"idx": 0, |
||||
|
"version": "6", |
||||
|
"when": 1737122352401, |
||||
|
"tag": "0000_short_skin", |
||||
|
"breakpoints": true |
||||
|
}, |
||||
|
{ |
||||
|
"idx": 1, |
||||
|
"version": "6", |
||||
|
"when": 1737122356601, |
||||
|
"tag": "0001_classy_the_stranger", |
||||
|
"breakpoints": true |
||||
|
} |
||||
|
] |
||||
|
} |
@ -0,0 +1,37 @@ |
|||||
|
import { sql, relations } from 'drizzle-orm'; |
||||
|
import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core'; |
||||
|
|
||||
|
import { oneTimeLink } from '../../schema'; |
||||
|
|
||||
|
export const client = sqliteTable('clients_table', { |
||||
|
id: int().primaryKey({ autoIncrement: true }), |
||||
|
name: text().notNull(), |
||||
|
ipv4Address: text('ipv4_address').notNull().unique(), |
||||
|
ipv6Address: text('ipv6_address').notNull().unique(), |
||||
|
privateKey: text('private_key').notNull(), |
||||
|
publicKey: text('public_key').notNull(), |
||||
|
preSharedKey: text('pre_shared_key').notNull(), |
||||
|
expiresAt: text('expires_at'), |
||||
|
allowedIps: text('allowed_ips', { mode: 'json' }).$type<string[]>().notNull(), |
||||
|
serverAllowedIps: text('server_allowed_ips', { mode: 'json' }) |
||||
|
.$type<string[]>() |
||||
|
.notNull(), |
||||
|
persistentKeepalive: int('persistent_keepalive').notNull(), |
||||
|
mtu: int().notNull(), |
||||
|
dns: text({ mode: 'json' }).$type<string[]>().notNull(), |
||||
|
enabled: int({ mode: 'boolean' }).notNull(), |
||||
|
createdAt: text('created_at') |
||||
|
.notNull() |
||||
|
.default(sql`(CURRENT_TIMESTAMP)`), |
||||
|
updatedAt: text('updated_at') |
||||
|
.notNull() |
||||
|
.default(sql`(CURRENT_TIMESTAMP)`) |
||||
|
.$onUpdate(() => sql`(CURRENT_TIMESTAMP)`), |
||||
|
}); |
||||
|
|
||||
|
export const clientsRelations = relations(client, ({ one }) => ({ |
||||
|
oneTimeLink: one(oneTimeLink, { |
||||
|
fields: [client.id], |
||||
|
references: [oneTimeLink.clientId], |
||||
|
}), |
||||
|
})); |
@ -0,0 +1,128 @@ |
|||||
|
import type { DBType } from '#db/sqlite'; |
||||
|
import { eq, sql } from 'drizzle-orm'; |
||||
|
import { client } from './schema'; |
||||
|
import type { ClientCreateType, UpdateClientType } from './types'; |
||||
|
import type { ID } from '#db/schema'; |
||||
|
import { wgInterface, userConfig } from '#db/schema'; |
||||
|
import { parseCidr } from 'cidr-tools'; |
||||
|
|
||||
|
function createPreparedStatement(db: DBType) { |
||||
|
return { |
||||
|
findAll: db.query.client |
||||
|
.findMany({ |
||||
|
with: { |
||||
|
oneTimeLink: true, |
||||
|
}, |
||||
|
}) |
||||
|
.prepare(), |
||||
|
findById: db.query.client |
||||
|
.findFirst({ where: eq(client.id, sql.placeholder('id')) }) |
||||
|
.prepare(), |
||||
|
toggle: db |
||||
|
.update(client) |
||||
|
.set({ enabled: sql.placeholder('enabled') as never as boolean }) |
||||
|
.where(eq(client.id, sql.placeholder('id'))) |
||||
|
.prepare(), |
||||
|
delete: db |
||||
|
.delete(client) |
||||
|
.where(eq(client.id, sql.placeholder('id'))) |
||||
|
.prepare(), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export class ClientService { |
||||
|
#db: DBType; |
||||
|
#statements: ReturnType<typeof createPreparedStatement>; |
||||
|
|
||||
|
constructor(db: DBType) { |
||||
|
this.#db = db; |
||||
|
this.#statements = createPreparedStatement(db); |
||||
|
} |
||||
|
|
||||
|
async getAll() { |
||||
|
const result = await this.#statements.findAll.execute(); |
||||
|
return result.map((row) => ({ |
||||
|
...row, |
||||
|
createdAt: new Date(row.createdAt), |
||||
|
updatedAt: new Date(row.updatedAt), |
||||
|
})); |
||||
|
} |
||||
|
|
||||
|
get(id: ID) { |
||||
|
return this.#statements.findById.execute({ id }); |
||||
|
} |
||||
|
|
||||
|
async create({ name, expiresAt }: ClientCreateType) { |
||||
|
const privateKey = await wg.generatePrivateKey(); |
||||
|
const publicKey = await wg.getPublicKey(privateKey); |
||||
|
const preSharedKey = await wg.generatePreSharedKey(); |
||||
|
|
||||
|
let parsedExpiresAt = expiresAt; |
||||
|
if (parsedExpiresAt) { |
||||
|
const expiresAtDate = new Date(parsedExpiresAt); |
||||
|
expiresAtDate.setHours(23); |
||||
|
expiresAtDate.setMinutes(59); |
||||
|
expiresAtDate.setSeconds(59); |
||||
|
parsedExpiresAt = expiresAtDate.toISOString(); |
||||
|
} |
||||
|
|
||||
|
return this.#db.transaction(async (tx) => { |
||||
|
const clients = await tx.query.client.findMany().execute(); |
||||
|
const clientInterface = await tx.query.wgInterface |
||||
|
.findFirst({ |
||||
|
where: eq(wgInterface.name, 'wg0'), |
||||
|
}) |
||||
|
.execute(); |
||||
|
|
||||
|
if (!clientInterface) { |
||||
|
throw new Error('WireGuard interface not found'); |
||||
|
} |
||||
|
|
||||
|
const clientConfig = await tx.query.userConfig |
||||
|
.findFirst({ |
||||
|
where: eq(userConfig.id, clientInterface.name), |
||||
|
}) |
||||
|
.execute(); |
||||
|
|
||||
|
if (!clientConfig) { |
||||
|
throw new Error('WireGuard interface configuration not found'); |
||||
|
} |
||||
|
|
||||
|
const ipv4Cidr = parseCidr(clientInterface.ipv4Cidr); |
||||
|
const ipv4Address = nextIP(4, ipv4Cidr, clients); |
||||
|
const ipv6Cidr = parseCidr(clientInterface.ipv6Cidr); |
||||
|
const ipv6Address = nextIP(6, ipv6Cidr, clients); |
||||
|
|
||||
|
await tx |
||||
|
.insert(client) |
||||
|
.values({ |
||||
|
name, |
||||
|
expiresAt: parsedExpiresAt, |
||||
|
privateKey, |
||||
|
publicKey, |
||||
|
preSharedKey, |
||||
|
ipv4Address, |
||||
|
ipv6Address, |
||||
|
mtu: clientConfig.defaultMtu, |
||||
|
allowedIps: clientConfig.defaultAllowedIps, |
||||
|
dns: clientConfig.defaultDns, |
||||
|
persistentKeepalive: clientConfig.defaultPersistentKeepalive, |
||||
|
serverAllowedIps: [], |
||||
|
enabled: true, |
||||
|
}) |
||||
|
.execute(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
toggle(id: ID, enabled: boolean) { |
||||
|
return this.#statements.toggle.execute({ id, enabled }); |
||||
|
} |
||||
|
|
||||
|
delete(id: ID) { |
||||
|
return this.#statements.delete.execute({ id }); |
||||
|
} |
||||
|
|
||||
|
update(id: ID, data: UpdateClientType) { |
||||
|
return this.#db.update(client).set(data).where(eq(client.id, id)).execute(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,72 @@ |
|||||
|
import type { InferSelectModel } from 'drizzle-orm'; |
||||
|
import z from 'zod'; |
||||
|
|
||||
|
import type { client } from './schema'; |
||||
|
|
||||
|
export type ID = string; |
||||
|
|
||||
|
export type ClientType = InferSelectModel<typeof client>; |
||||
|
|
||||
|
export type CreateClientType = Omit< |
||||
|
ClientType, |
||||
|
'createdAt' | 'updatedAt' | 'id' |
||||
|
>; |
||||
|
|
||||
|
export type UpdateClientType = Omit< |
||||
|
CreateClientType, |
||||
|
'privateKey' | 'publicKey' | 'preSharedKey' |
||||
|
>; |
||||
|
|
||||
|
const name = z |
||||
|
.string({ message: 'zod.client.name' }) |
||||
|
.min(1, 'zod.client.nameMin') |
||||
|
.pipe(safeStringRefine); |
||||
|
|
||||
|
const expiresAt = z |
||||
|
.string({ message: 'zod.client.expireDate' }) |
||||
|
.min(1, 'zod.client.expireDateMin') |
||||
|
.pipe(safeStringRefine) |
||||
|
.nullable(); |
||||
|
|
||||
|
const address4 = z |
||||
|
.string({ message: 'zod.client.address4' }) |
||||
|
.min(1, { message: 'zod.client.address4Min' }) |
||||
|
.pipe(safeStringRefine); |
||||
|
|
||||
|
const address6 = z |
||||
|
.string({ message: 'zod.client.address6' }) |
||||
|
.min(1, { message: 'zod.client.address6Min' }) |
||||
|
.pipe(safeStringRefine); |
||||
|
|
||||
|
const serverAllowedIps = z.array(AddressSchema, { |
||||
|
message: 'zod.serverAllowedIps', |
||||
|
}); |
||||
|
|
||||
|
export const ClientCreateSchema = z.object({ |
||||
|
name: name, |
||||
|
expiresAt: expiresAt, |
||||
|
}); |
||||
|
|
||||
|
export type ClientCreateType = z.infer<typeof ClientCreateSchema>; |
||||
|
|
||||
|
export const ClientUpdateSchema = schemaForType<UpdateClientType>()( |
||||
|
z.object({ |
||||
|
name: name, |
||||
|
enabled: EnabledSchema, |
||||
|
expiresAt: expiresAt, |
||||
|
ipv4Address: address4, |
||||
|
ipv6Address: address6, |
||||
|
allowedIps: AllowedIpsSchema, |
||||
|
serverAllowedIps: serverAllowedIps, |
||||
|
mtu: MtuSchema, |
||||
|
persistentKeepalive: PersistentKeepaliveSchema, |
||||
|
dns: DnsSchema, |
||||
|
}) |
||||
|
); |
||||
|
|
||||
|
// TODO: investigate if coerce is bad
|
||||
|
const clientId = z.number({ message: 'zod.client.id', coerce: true }); |
||||
|
|
||||
|
export const ClientGetSchema = z.object({ |
||||
|
clientId: clientId, |
||||
|
}); |
@ -0,0 +1,16 @@ |
|||||
|
import { sql } from 'drizzle-orm'; |
||||
|
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(), |
||||
|
createdAt: text('created_at') |
||||
|
.notNull() |
||||
|
.default(sql`(CURRENT_TIMESTAMP)`), |
||||
|
updatedAt: text('updated_at') |
||||
|
.notNull() |
||||
|
.default(sql`(CURRENT_TIMESTAMP)`) |
||||
|
.$onUpdate(() => sql`(CURRENT_TIMESTAMP)`), |
||||
|
}); |
@ -0,0 +1,68 @@ |
|||||
|
import type { DBType } from '#db/sqlite'; |
||||
|
import { sql } from 'drizzle-orm'; |
||||
|
import { general } from './schema'; |
||||
|
import type { GeneralUpdateType } from './types'; |
||||
|
|
||||
|
function createPreparedStatement(db: DBType) { |
||||
|
return { |
||||
|
find: db.query.general.findFirst().prepare(), |
||||
|
updateSetupStep: db |
||||
|
.update(general) |
||||
|
.set({ |
||||
|
setupStep: sql.placeholder('setupStep') as never as number, |
||||
|
}) |
||||
|
.prepare(), |
||||
|
update: db |
||||
|
.update(general) |
||||
|
.set({ |
||||
|
sessionTimeout: sql.placeholder('sessionTimeout') as never as number, |
||||
|
}) |
||||
|
.prepare(), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export class GeneralService { |
||||
|
#statements: ReturnType<typeof createPreparedStatement>; |
||||
|
|
||||
|
constructor(db: DBType) { |
||||
|
this.#statements = createPreparedStatement(db); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @throws |
||||
|
*/ |
||||
|
private async get() { |
||||
|
const result = await this.#statements.find.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 }; |
||||
|
} |
||||
|
|
||||
|
setSetupStep(step: number) { |
||||
|
return this.#statements.updateSetupStep.execute({ setupStep: step }); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @throws |
||||
|
*/ |
||||
|
async getSessionConfig() { |
||||
|
const result = await this.get(); |
||||
|
return { |
||||
|
sessionPassword: result.sessionPassword, |
||||
|
sessionTimeout: result.sessionTimeout, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
update(data: GeneralUpdateType) { |
||||
|
return this.#statements.update.execute(data); |
||||
|
} |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
import type { InferSelectModel } from 'drizzle-orm'; |
||||
|
import type { general } from './schema'; |
||||
|
import z from 'zod'; |
||||
|
|
||||
|
export type GeneralType = InferSelectModel<typeof general>; |
||||
|
|
||||
|
const sessionTimeout = z.number({ message: 'zod.general.sessionTimeout' }); |
||||
|
|
||||
|
export const GeneralUpdateSchema = z.object({ |
||||
|
sessionTimeout: sessionTimeout, |
||||
|
}); |
||||
|
|
||||
|
export type GeneralUpdateType = z.infer<typeof GeneralUpdateSchema>; |
||||
|
|
||||
|
export type SetupStepType = { step: number; done: boolean }; |
@ -0,0 +1,24 @@ |
|||||
|
import { sql } from 'drizzle-orm'; |
||||
|
import { sqliteTable, text } from 'drizzle-orm/sqlite-core'; |
||||
|
|
||||
|
import { wgInterface } from '../../schema'; |
||||
|
|
||||
|
export const hooks = sqliteTable('hooks_table', { |
||||
|
id: text() |
||||
|
.primaryKey() |
||||
|
.references(() => wgInterface.name, { |
||||
|
onDelete: 'cascade', |
||||
|
onUpdate: 'cascade', |
||||
|
}), |
||||
|
preUp: text('pre_up').notNull(), |
||||
|
postUp: text('post_up').notNull(), |
||||
|
preDown: text('pre_down').notNull(), |
||||
|
postDown: text('post_down').notNull(), |
||||
|
createdAt: text('created_at') |
||||
|
.notNull() |
||||
|
.default(sql`(CURRENT_TIMESTAMP)`), |
||||
|
updatedAt: text('updated_at') |
||||
|
.notNull() |
||||
|
.default(sql`(CURRENT_TIMESTAMP)`) |
||||
|
.$onUpdate(() => sql`(CURRENT_TIMESTAMP)`), |
||||
|
}); |
@ -0,0 +1,34 @@ |
|||||
|
import type { DBType } from '#db/sqlite'; |
||||
|
import { eq, sql } from 'drizzle-orm'; |
||||
|
import { hooks } from './schema'; |
||||
|
import type { HooksUpdateType } from './types'; |
||||
|
|
||||
|
function createPreparedStatement(db: DBType) { |
||||
|
return { |
||||
|
get: db.query.hooks |
||||
|
.findFirst({ where: eq(hooks.id, sql.placeholder('interface')) }) |
||||
|
.prepare(), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export class HooksService { |
||||
|
#db: DBType; |
||||
|
#statements: ReturnType<typeof createPreparedStatement>; |
||||
|
|
||||
|
constructor(db: DBType) { |
||||
|
this.#db = db; |
||||
|
this.#statements = createPreparedStatement(db); |
||||
|
} |
||||
|
|
||||
|
get(infName: string) { |
||||
|
return this.#statements.get.execute({ interface: infName }); |
||||
|
} |
||||
|
|
||||
|
update(infName: string, data: HooksUpdateType) { |
||||
|
return this.#db |
||||
|
.update(hooks) |
||||
|
.set(data) |
||||
|
.where(eq(hooks.id, infName)) |
||||
|
.execute(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
import type { InferSelectModel } from 'drizzle-orm'; |
||||
|
import type { hooks } from './schema'; |
||||
|
import z from 'zod'; |
||||
|
|
||||
|
export type HooksType = InferSelectModel<typeof hooks>; |
||||
|
|
||||
|
export type HooksUpdateType = Omit<HooksType, 'id' | 'createdAt' | 'updatedAt'>; |
||||
|
|
||||
|
const hook = z.string({ message: 'zod.hook' }).pipe(safeStringRefine); |
||||
|
|
||||
|
export const HooksUpdateSchema = schemaForType<HooksUpdateType>()( |
||||
|
z.object({ |
||||
|
preUp: hook, |
||||
|
postUp: hook, |
||||
|
preDown: hook, |
||||
|
postDown: hook, |
||||
|
}) |
||||
|
); |
@ -0,0 +1,39 @@ |
|||||
|
import { sql, relations } from 'drizzle-orm'; |
||||
|
import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core'; |
||||
|
|
||||
|
import { userConfig, hooks, prometheus } from '../../schema'; |
||||
|
|
||||
|
// maybe support multiple interfaces in the future
|
||||
|
export const wgInterface = sqliteTable('interfaces_table', { |
||||
|
name: text().primaryKey(), |
||||
|
device: text().notNull(), |
||||
|
port: int().notNull().unique(), |
||||
|
privateKey: text('private_key').notNull(), |
||||
|
publicKey: text('public_key').notNull(), |
||||
|
ipv4Cidr: text('ipv4_cidr').notNull(), |
||||
|
ipv6Cidr: text('ipv6_cidr').notNull(), |
||||
|
mtu: int().notNull(), |
||||
|
enabled: int({ mode: 'boolean' }).notNull(), |
||||
|
createdAt: text('created_at') |
||||
|
.notNull() |
||||
|
.default(sql`(CURRENT_TIMESTAMP)`), |
||||
|
updatedAt: text('updated_at') |
||||
|
.notNull() |
||||
|
.default(sql`(CURRENT_TIMESTAMP)`) |
||||
|
.$onUpdate(() => sql`(CURRENT_TIMESTAMP)`), |
||||
|
}); |
||||
|
|
||||
|
export const wgInterfaceRelations = relations(wgInterface, ({ one }) => ({ |
||||
|
hooks: one(hooks, { |
||||
|
fields: [wgInterface.name], |
||||
|
references: [hooks.id], |
||||
|
}), |
||||
|
prometheus: one(prometheus, { |
||||
|
fields: [wgInterface.name], |
||||
|
references: [prometheus.id], |
||||
|
}), |
||||
|
userConfig: one(userConfig, { |
||||
|
fields: [wgInterface.name], |
||||
|
references: [userConfig.id], |
||||
|
}), |
||||
|
})); |
@ -0,0 +1,90 @@ |
|||||
|
import type { DBType } from '#db/sqlite'; |
||||
|
import isCidr from 'is-cidr'; |
||||
|
import { eq, sql } from 'drizzle-orm'; |
||||
|
import { wgInterface } from './schema'; |
||||
|
import type { InterfaceCidrUpdateType, InterfaceUpdateType } from './types'; |
||||
|
import { client as clientSchema } from '#db/schema'; |
||||
|
import { parseCidr } from 'cidr-tools'; |
||||
|
|
||||
|
function createPreparedStatement(db: DBType) { |
||||
|
return { |
||||
|
get: db.query.wgInterface |
||||
|
.findFirst({ where: eq(wgInterface.name, sql.placeholder('interface')) }) |
||||
|
.prepare(), |
||||
|
getAll: db.query.wgInterface.findMany().prepare(), |
||||
|
updateKeyPair: db |
||||
|
.update(wgInterface) |
||||
|
.set({ |
||||
|
privateKey: sql.placeholder('privateKey') as never as string, |
||||
|
publicKey: sql.placeholder('publicKey') as never as string, |
||||
|
}) |
||||
|
.where(eq(wgInterface.name, sql.placeholder('interface'))) |
||||
|
.prepare(), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export class InterfaceService { |
||||
|
#db: DBType; |
||||
|
#statements: ReturnType<typeof createPreparedStatement>; |
||||
|
|
||||
|
constructor(db: DBType) { |
||||
|
this.#db = db; |
||||
|
this.#statements = createPreparedStatement(db); |
||||
|
} |
||||
|
|
||||
|
get(infName: string) { |
||||
|
return this.#statements.get.execute({ interface: infName }); |
||||
|
} |
||||
|
|
||||
|
getAll() { |
||||
|
return this.#statements.getAll.execute(); |
||||
|
} |
||||
|
|
||||
|
updateKeyPair(infName: string, privateKey: string, publicKey: string) { |
||||
|
return this.#statements.updateKeyPair.execute({ |
||||
|
interface: infName, |
||||
|
privateKey, |
||||
|
publicKey, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
update(infName: string, data: InterfaceUpdateType) { |
||||
|
return this.#db |
||||
|
.update(wgInterface) |
||||
|
.set(data) |
||||
|
.where(eq(wgInterface.name, infName)) |
||||
|
.execute(); |
||||
|
} |
||||
|
|
||||
|
updateCidr(infName: string, data: InterfaceCidrUpdateType) { |
||||
|
if (!isCidr(data.ipv4Cidr) || !isCidr(data.ipv6Cidr)) { |
||||
|
throw new Error('Invalid CIDR'); |
||||
|
} |
||||
|
return this.#db.transaction(async (tx) => { |
||||
|
await tx |
||||
|
.update(wgInterface) |
||||
|
.set(data) |
||||
|
.where(eq(wgInterface.name, infName)) |
||||
|
.execute(); |
||||
|
|
||||
|
const clients = await tx.query.client.findMany().execute(); |
||||
|
|
||||
|
for (const client of clients) { |
||||
|
// TODO: optimize
|
||||
|
const clients = await tx.query.client.findMany().execute(); |
||||
|
|
||||
|
const nextIpv4 = nextIP(4, parseCidr(data.ipv4Cidr), clients); |
||||
|
const nextIpv6 = nextIP(6, parseCidr(data.ipv6Cidr), clients); |
||||
|
|
||||
|
await tx |
||||
|
.update(clientSchema) |
||||
|
.set({ |
||||
|
ipv4Address: nextIpv4, |
||||
|
ipv6Address: nextIpv6, |
||||
|
}) |
||||
|
.where(eq(clientSchema.id, client.id)) |
||||
|
.execute(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
@ -0,0 +1,49 @@ |
|||||
|
import type { InferSelectModel } from 'drizzle-orm'; |
||||
|
import type { wgInterface } from './schema'; |
||||
|
import z from 'zod'; |
||||
|
|
||||
|
export type InterfaceType = InferSelectModel<typeof wgInterface>; |
||||
|
|
||||
|
export type InterfaceCreateType = Omit< |
||||
|
InterfaceType, |
||||
|
'createdAt' | 'updatedAt' |
||||
|
>; |
||||
|
|
||||
|
export type InterfaceUpdateType = Omit< |
||||
|
InterfaceCreateType, |
||||
|
'name' | 'createdAt' | 'updatedAt' | 'privateKey' | 'publicKey' |
||||
|
>; |
||||
|
|
||||
|
const device = z |
||||
|
.string({ message: 'zod.interface.device' }) |
||||
|
.min(1, 'zod.interface.deviceMin') |
||||
|
.pipe(safeStringRefine); |
||||
|
|
||||
|
const cidr = z |
||||
|
.string({ message: 'zod.interface.cidr' }) |
||||
|
.min(1, { message: 'zod.interface.cidrMin' }) |
||||
|
.pipe(safeStringRefine); |
||||
|
|
||||
|
export const InterfaceUpdateSchema = schemaForType<InterfaceUpdateType>()( |
||||
|
z.object({ |
||||
|
ipv4Cidr: cidr, |
||||
|
ipv6Cidr: cidr, |
||||
|
mtu: MtuSchema, |
||||
|
port: PortSchema, |
||||
|
device: device, |
||||
|
enabled: EnabledSchema, |
||||
|
}) |
||||
|
); |
||||
|
|
||||
|
export type InterfaceCidrUpdateType = { |
||||
|
ipv4Cidr: string; |
||||
|
ipv6Cidr: string; |
||||
|
}; |
||||
|
|
||||
|
export const InterfaceCidrUpdateSchema = |
||||
|
schemaForType<InterfaceCidrUpdateType>()( |
||||
|
z.object({ |
||||
|
ipv4Cidr: cidr, |
||||
|
ipv6Cidr: cidr, |
||||
|
}) |
||||
|
); |
@ -0,0 +1,21 @@ |
|||||
|
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)`), |
||||
|
}); |
@ -0,0 +1,31 @@ |
|||||
|
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); |
||||
|
} |
||||
|
} |
@ -0,0 +1,4 @@ |
|||||
|
import type { InferSelectModel } from 'drizzle-orm'; |
||||
|
import type { prometheus } from './schema'; |
||||
|
|
||||
|
export type PrometheusType = InferSelectModel<typeof prometheus>; |
@ -0,0 +1,27 @@ |
|||||
|
import { sql, relations } from 'drizzle-orm'; |
||||
|
import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core'; |
||||
|
|
||||
|
import { client } from '../../schema'; |
||||
|
|
||||
|
export const oneTimeLink = sqliteTable('one_time_links_table', { |
||||
|
id: int().primaryKey({ autoIncrement: true }), |
||||
|
oneTimeLink: text('one_time_link').notNull().unique(), |
||||
|
expiresAt: text('expires_at').notNull(), |
||||
|
clientId: int() |
||||
|
.notNull() |
||||
|
.references(() => client.id, { onDelete: 'cascade', onUpdate: 'cascade' }), |
||||
|
createdAt: text('created_at') |
||||
|
.notNull() |
||||
|
.default(sql`(CURRENT_TIMESTAMP)`), |
||||
|
updatedAt: text('updated_at') |
||||
|
.notNull() |
||||
|
.default(sql`(CURRENT_TIMESTAMP)`) |
||||
|
.$onUpdate(() => sql`(CURRENT_TIMESTAMP)`), |
||||
|
}); |
||||
|
|
||||
|
export const oneTimeLinksRelations = relations(oneTimeLink, ({ one }) => ({ |
||||
|
client: one(client, { |
||||
|
fields: [oneTimeLink.clientId], |
||||
|
references: [client.id], |
||||
|
}), |
||||
|
})); |
@ -0,0 +1,52 @@ |
|||||
|
import type { DBType } from '#db/sqlite'; |
||||
|
import { eq, sql } from 'drizzle-orm'; |
||||
|
import { oneTimeLink } from './schema'; |
||||
|
import type { ID } from '../../schema'; |
||||
|
import CRC32 from 'crc-32'; |
||||
|
|
||||
|
function createPreparedStatement(db: DBType) { |
||||
|
return { |
||||
|
delete: db |
||||
|
.delete(oneTimeLink) |
||||
|
.where(eq(oneTimeLink.id, sql.placeholder('id'))) |
||||
|
.prepare(), |
||||
|
create: db |
||||
|
.insert(oneTimeLink) |
||||
|
.values({ |
||||
|
clientId: sql.placeholder('id'), |
||||
|
oneTimeLink: sql.placeholder('oneTimeLink'), |
||||
|
expiresAt: sql.placeholder('expiresAt'), |
||||
|
}) |
||||
|
.prepare(), |
||||
|
erase: db |
||||
|
.update(oneTimeLink) |
||||
|
.set({ expiresAt: sql.placeholder('expiresAt') as never as string }) |
||||
|
.where(eq(oneTimeLink.clientId, sql.placeholder('id'))) |
||||
|
.prepare(), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export class OneTimeLinkService { |
||||
|
#statements: ReturnType<typeof createPreparedStatement>; |
||||
|
|
||||
|
constructor(db: DBType) { |
||||
|
this.#statements = createPreparedStatement(db); |
||||
|
} |
||||
|
|
||||
|
delete(id: ID) { |
||||
|
return this.#statements.delete.execute({ id }); |
||||
|
} |
||||
|
|
||||
|
generate(id: ID) { |
||||
|
const key = `${id}-${Math.floor(Math.random() * 1000)}`; |
||||
|
const oneTimeLink = Math.abs(CRC32.str(key)).toString(16); |
||||
|
const expiresAt = new Date(Date.now() + 5 * 60 * 1000).toISOString(); |
||||
|
|
||||
|
return this.#statements.create.execute({ id, oneTimeLink, expiresAt }); |
||||
|
} |
||||
|
|
||||
|
erase(id: ID) { |
||||
|
const expiresAt = Date.now() + 10 * 1000; |
||||
|
return this.#statements.erase.execute({ id, expiresAt }); |
||||
|
} |
||||
|
} |
@ -0,0 +1,17 @@ |
|||||
|
import type { InferSelectModel } from 'drizzle-orm'; |
||||
|
import type { oneTimeLink } from './schema'; |
||||
|
import { z } from 'zod'; |
||||
|
|
||||
|
export type OneTimeLinkType = InferSelectModel<typeof oneTimeLink>; |
||||
|
|
||||
|
const oneTimeLinkType = z |
||||
|
.string({ message: 'zod.otl.otl' }) |
||||
|
.min(1, 'zod.otl.otlMin') |
||||
|
.pipe(safeStringRefine); |
||||
|
|
||||
|
export const OneTimeLinkGetSchema = z.object( |
||||
|
{ |
||||
|
oneTimeLink: oneTimeLinkType, |
||||
|
}, |
||||
|
{ message: objectMessage } |
||||
|
); |
@ -0,0 +1,19 @@ |
|||||
|
import { sql } from 'drizzle-orm'; |
||||
|
import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core'; |
||||
|
|
||||
|
export const user = sqliteTable('users_table', { |
||||
|
id: int().primaryKey({ autoIncrement: true }), |
||||
|
username: text().notNull().unique(), |
||||
|
password: text().notNull(), |
||||
|
email: text(), |
||||
|
name: text().notNull(), |
||||
|
role: int().$type<Role>().notNull(), |
||||
|
enabled: int({ mode: 'boolean' }).notNull(), |
||||
|
createdAt: text('created_at') |
||||
|
.notNull() |
||||
|
.default(sql`(CURRENT_TIMESTAMP)`), |
||||
|
updatedAt: text('updated_at') |
||||
|
.notNull() |
||||
|
.default(sql`(CURRENT_TIMESTAMP)`) |
||||
|
.$onUpdate(() => sql`(CURRENT_TIMESTAMP)`), |
||||
|
}); |
@ -0,0 +1,63 @@ |
|||||
|
import type { DBType } from '#db/sqlite'; |
||||
|
import { eq, sql } from 'drizzle-orm'; |
||||
|
import { user } from './schema'; |
||||
|
import type { ID } from '../../schema'; |
||||
|
|
||||
|
function createPreparedStatement(db: DBType) { |
||||
|
return { |
||||
|
findAll: db.query.user.findMany().prepare(), |
||||
|
findById: db.query.user |
||||
|
.findFirst({ where: eq(user.id, sql.placeholder('id')) }) |
||||
|
.prepare(), |
||||
|
findByUsername: db.query.user |
||||
|
.findFirst({ |
||||
|
where: eq(user.username, sql.placeholder('username')), |
||||
|
}) |
||||
|
.prepare(), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export class UserService { |
||||
|
#db: DBType; |
||||
|
#statements: ReturnType<typeof createPreparedStatement>; |
||||
|
|
||||
|
constructor(db: DBType) { |
||||
|
this.#db = db; |
||||
|
this.#statements = createPreparedStatement(db); |
||||
|
} |
||||
|
|
||||
|
async getAll() { |
||||
|
return this.#statements.findAll.execute(); |
||||
|
} |
||||
|
|
||||
|
async get(id: ID) { |
||||
|
return this.#statements.findById.execute({ id }); |
||||
|
} |
||||
|
|
||||
|
async getByUsername(username: string) { |
||||
|
return this.#statements.findByUsername.execute({ username }); |
||||
|
} |
||||
|
|
||||
|
async create(username: string, password: string) { |
||||
|
const hash = await hashPassword(password); |
||||
|
|
||||
|
return this.#db.transaction(async (tx) => { |
||||
|
const oldUser = await this.getByUsername(username); |
||||
|
|
||||
|
if (oldUser) { |
||||
|
throw new Error('User already exists'); |
||||
|
} |
||||
|
|
||||
|
const userCount = await tx.$count(user); |
||||
|
|
||||
|
await tx.insert(user).values({ |
||||
|
password: hash, |
||||
|
username, |
||||
|
email: null, |
||||
|
name: 'Administrator', |
||||
|
role: userCount === 0 ? roles.ADMIN : roles.CLIENT, |
||||
|
enabled: true, |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
} |
@ -0,0 +1,43 @@ |
|||||
|
import type { InferSelectModel } from 'drizzle-orm'; |
||||
|
import type { user } from './schema'; |
||||
|
import z from 'zod'; |
||||
|
|
||||
|
export type UserType = InferSelectModel<typeof user>; |
||||
|
|
||||
|
const username = z |
||||
|
.string({ message: 'zod.user.username' }) |
||||
|
.min(8, 'zod.user.usernameMin') |
||||
|
.pipe(safeStringRefine); |
||||
|
|
||||
|
const password = z |
||||
|
.string({ message: 'zod.user.password' }) |
||||
|
.min(12, 'zod.user.passwordMin') |
||||
|
.regex(/[A-Z]/, 'zod.user.passwordUppercase') |
||||
|
.regex(/[a-z]/, 'zod.user.passwordLowercase') |
||||
|
.regex(/\d/, 'zod.user.passwordNumber') |
||||
|
.regex(/[!@#$%^&*(),.?":{}|<>]/, 'zod.user.passwordSpecial') |
||||
|
.pipe(safeStringRefine); |
||||
|
|
||||
|
const remember = z.boolean({ message: 'zod.user.remember' }); |
||||
|
|
||||
|
export const UserLoginSchema = z.object( |
||||
|
{ |
||||
|
username: username, |
||||
|
password: password, |
||||
|
remember: remember, |
||||
|
}, |
||||
|
{ message: objectMessage } |
||||
|
); |
||||
|
|
||||
|
const accept = z.boolean().refine((val) => val === true, { |
||||
|
message: 'zod.user.accept', |
||||
|
}); |
||||
|
|
||||
|
export const UserSetupType = z.object( |
||||
|
{ |
||||
|
username: username, |
||||
|
password: password, |
||||
|
accept: accept, |
||||
|
}, |
||||
|
{ message: objectMessage } |
||||
|
); |
@ -0,0 +1,29 @@ |
|||||
|
import { sql } from 'drizzle-orm'; |
||||
|
import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core'; |
||||
|
|
||||
|
import { wgInterface } from '../../schema'; |
||||
|
|
||||
|
// default* means clients store it themselves
|
||||
|
export const userConfig = sqliteTable('user_configs_table', { |
||||
|
id: text() |
||||
|
.primaryKey() |
||||
|
.references(() => wgInterface.name, { |
||||
|
onDelete: 'cascade', |
||||
|
onUpdate: 'cascade', |
||||
|
}), |
||||
|
defaultMtu: int('default_mtu').notNull(), |
||||
|
defaultPersistentKeepalive: int('default_persistent_keepalive').notNull(), |
||||
|
defaultDns: text('default_dns', { mode: 'json' }).$type<string[]>().notNull(), |
||||
|
defaultAllowedIps: text('default_allowed_ips', { mode: 'json' }) |
||||
|
.$type<string[]>() |
||||
|
.notNull(), |
||||
|
host: text().notNull(), |
||||
|
port: int().notNull(), |
||||
|
createdAt: text('created_at') |
||||
|
.notNull() |
||||
|
.default(sql`(CURRENT_TIMESTAMP)`), |
||||
|
updatedAt: text('updated_at') |
||||
|
.notNull() |
||||
|
.default(sql`(CURRENT_TIMESTAMP)`) |
||||
|
.$onUpdate(() => sql`(CURRENT_TIMESTAMP)`), |
||||
|
}); |
@ -0,0 +1,50 @@ |
|||||
|
import type { DBType } from '#db/sqlite'; |
||||
|
import { eq, sql } from 'drizzle-orm'; |
||||
|
import { userConfig } from './schema'; |
||||
|
import type { UserConfigUpdateType } from './types'; |
||||
|
|
||||
|
function createPreparedStatement(db: DBType) { |
||||
|
return { |
||||
|
get: db.query.userConfig |
||||
|
.findFirst({ where: eq(userConfig.id, sql.placeholder('interface')) }) |
||||
|
.prepare(), |
||||
|
updateHostPort: db |
||||
|
.update(userConfig) |
||||
|
.set({ |
||||
|
host: sql.placeholder('host') as never as string, |
||||
|
port: sql.placeholder('port') as never as number, |
||||
|
}) |
||||
|
.where(eq(userConfig.id, sql.placeholder('interface'))) |
||||
|
.prepare(), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export class UserConfigService { |
||||
|
#db: DBType; |
||||
|
#statements: ReturnType<typeof createPreparedStatement>; |
||||
|
|
||||
|
constructor(db: DBType) { |
||||
|
this.#db = db; |
||||
|
this.#statements = createPreparedStatement(db); |
||||
|
} |
||||
|
|
||||
|
get(infName: string) { |
||||
|
return this.#statements.get.execute({ interface: infName }); |
||||
|
} |
||||
|
|
||||
|
updateHostPort(infName: string, host: string, port: number) { |
||||
|
return this.#statements.updateHostPort.execute({ |
||||
|
interface: infName, |
||||
|
host, |
||||
|
port, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
update(infName: string, data: UserConfigUpdateType) { |
||||
|
return this.#db |
||||
|
.update(userConfig) |
||||
|
.set(data) |
||||
|
.where(eq(userConfig.id, infName)) |
||||
|
.execute(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,31 @@ |
|||||
|
import type { InferSelectModel } from 'drizzle-orm'; |
||||
|
import type { userConfig } from './schema'; |
||||
|
import z from 'zod'; |
||||
|
|
||||
|
export type UserConfigType = InferSelectModel<typeof userConfig>; |
||||
|
|
||||
|
const host = z |
||||
|
.string({ message: 'zod.userConfig.host' }) |
||||
|
.min(1, 'zod.userConfig.hostMin') |
||||
|
.pipe(safeStringRefine); |
||||
|
|
||||
|
export const UserConfigSetupType = z.object({ |
||||
|
host: host, |
||||
|
port: PortSchema, |
||||
|
}); |
||||
|
|
||||
|
export type UserConfigUpdateType = Omit< |
||||
|
UserConfigType, |
||||
|
'id' | 'createdAt' | 'updatedAt' |
||||
|
>; |
||||
|
|
||||
|
export const UserConfigUpdateSchema = schemaForType<UserConfigUpdateType>()( |
||||
|
z.object({ |
||||
|
port: PortSchema, |
||||
|
defaultMtu: MtuSchema, |
||||
|
defaultPersistentKeepalive: PersistentKeepaliveSchema, |
||||
|
defaultDns: DnsSchema, |
||||
|
defaultAllowedIps: AllowedIpsSchema, |
||||
|
host: host, |
||||
|
}) |
||||
|
); |
@ -0,0 +1,12 @@ |
|||||
|
// Make sure to not use any Path Aliases in these files
|
||||
|
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'; |
||||
|
|
||||
|
// TODO: move to types
|
||||
|
export type ID = number; |
@ -0,0 +1,63 @@ |
|||||
|
import { drizzle } from 'drizzle-orm/libsql'; |
||||
|
import { migrate as drizzleMigrate } from 'drizzle-orm/libsql/migrator'; |
||||
|
import { createClient } from '@libsql/client'; |
||||
|
import debug from 'debug'; |
||||
|
|
||||
|
import * as schema from './schema'; |
||||
|
import { ClientService } from './repositories/client/service'; |
||||
|
import { GeneralService } from './repositories/general/service'; |
||||
|
import { UserService } from './repositories/user/service'; |
||||
|
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'); |
||||
|
|
||||
|
const client = createClient({ url: 'file:/etc/wireguard/wg0.db' }); |
||||
|
const db = drizzle({ client, schema }); |
||||
|
|
||||
|
export async function connect() { |
||||
|
await migrate(); |
||||
|
return new DBService(db); |
||||
|
} |
||||
|
|
||||
|
class DBService { |
||||
|
clients: ClientService; |
||||
|
general: GeneralService; |
||||
|
users: UserService; |
||||
|
userConfigs: UserConfigService; |
||||
|
interfaces: InterfaceService; |
||||
|
hooks: HooksService; |
||||
|
oneTimeLinks: OneTimeLinkService; |
||||
|
metrics: MetricsService; |
||||
|
|
||||
|
constructor(db: DBType) { |
||||
|
this.clients = new ClientService(db); |
||||
|
this.general = new GeneralService(db); |
||||
|
this.users = new UserService(db); |
||||
|
this.userConfigs = new UserConfigService(db); |
||||
|
this.interfaces = new InterfaceService(db); |
||||
|
this.hooks = new HooksService(db); |
||||
|
this.oneTimeLinks = new OneTimeLinkService(db); |
||||
|
this.metrics = new MetricsService(db); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export type DBType = typeof db; |
||||
|
export type DBServiceType = DBService; |
||||
|
|
||||
|
async function migrate() { |
||||
|
try { |
||||
|
DB_DEBUG('Migrating database...'); |
||||
|
await drizzleMigrate(db, { |
||||
|
migrationsFolder: './server/database/migrations', |
||||
|
}); |
||||
|
DB_DEBUG('Migration complete'); |
||||
|
} catch (e) { |
||||
|
if (e instanceof Error) { |
||||
|
DB_DEBUG('Failed to migrate database:', e.message); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -1,35 +0,0 @@ |
|||||
export default defineEventHandler(async (event) => { |
|
||||
const url = getRequestURL(event); |
|
||||
const session = await useWGSession(event); |
|
||||
|
|
||||
// Api handled by session, Setup handled with setup middleware
|
|
||||
if (url.pathname.startsWith('/api/') || url.pathname.startsWith('/setup')) { |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (url.pathname === '/login') { |
|
||||
if (session.data.userId) { |
|
||||
return sendRedirect(event, '/', 302); |
|
||||
} |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// Require auth for every page other than Login
|
|
||||
// TODO: investigate /__nuxt_error (error page when unauthenticated)
|
|
||||
if (!session.data.userId) { |
|
||||
return sendRedirect(event, '/login', 302); |
|
||||
} |
|
||||
|
|
||||
if (url.pathname.startsWith('/admin')) { |
|
||||
const user = await Database.user.findById(session.data.userId); |
|
||||
if (!user) { |
|
||||
return sendRedirect(event, '/login', 302); |
|
||||
} |
|
||||
if (user.role !== 'ADMIN') { |
|
||||
throw createError({ |
|
||||
statusCode: 403, |
|
||||
statusMessage: 'Not allowed to access Admin Panel', |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
}); |
|
@ -1,94 +0,0 @@ |
|||||
import type { User } from '~~/services/database/repositories/user'; |
|
||||
|
|
||||
export default defineEventHandler(async (event) => { |
|
||||
const url = getRequestURL(event); |
|
||||
// If one method of a route is public, every method is public!
|
|
||||
// Handle api routes
|
|
||||
if ( |
|
||||
!url.pathname.startsWith('/api/') || |
|
||||
url.pathname.startsWith('/api/setup/') || |
|
||||
url.pathname === '/api/session' || |
|
||||
url.pathname === '/api/release' |
|
||||
) { |
|
||||
return; |
|
||||
} |
|
||||
const system = await Database.system.get(); |
|
||||
|
|
||||
const session = await getSession<WGSession>(event, system.sessionConfig); |
|
||||
const authorization = getHeader(event, 'Authorization'); |
|
||||
|
|
||||
let user: User | undefined = undefined; |
|
||||
if (session.data.userId) { |
|
||||
// Handle if authenticating using Session
|
|
||||
user = await Database.user.findById(session.data.userId); |
|
||||
} else if (authorization) { |
|
||||
// Handle if authenticating using Header
|
|
||||
const [method, value] = authorization.split(' '); |
|
||||
// Support Basic Authentication
|
|
||||
// TODO: support personal access token or similar
|
|
||||
if (method !== 'Basic' || !value) { |
|
||||
throw createError({ |
|
||||
statusCode: 401, |
|
||||
statusMessage: 'Session failed', |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
const basicValue = Buffer.from(value, 'base64').toString('utf-8'); |
|
||||
|
|
||||
// Split by first ":"
|
|
||||
const index = basicValue.indexOf(':'); |
|
||||
const username = basicValue.substring(0, index); |
|
||||
const password = basicValue.substring(index + 1); |
|
||||
|
|
||||
if (!username || !password) { |
|
||||
throw createError({ |
|
||||
statusCode: 401, |
|
||||
statusMessage: 'Session failed', |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
const users = await Database.user.findAll(); |
|
||||
const foundUser = users.find((v) => v.username === username); |
|
||||
|
|
||||
if (!foundUser) { |
|
||||
throw createError({ |
|
||||
statusCode: 401, |
|
||||
statusMessage: 'Session failed', |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
const userHashPassword = foundUser.password; |
|
||||
const passwordValid = await isPasswordValid(password, userHashPassword); |
|
||||
|
|
||||
if (!passwordValid) { |
|
||||
throw createError({ |
|
||||
statusCode: 401, |
|
||||
statusMessage: 'Incorrect Password', |
|
||||
}); |
|
||||
} |
|
||||
user = foundUser; |
|
||||
} |
|
||||
|
|
||||
if (!user) { |
|
||||
throw createError({ |
|
||||
statusCode: 401, |
|
||||
statusMessage: 'Not logged in', |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
if (!user.enabled) { |
|
||||
throw createError({ |
|
||||
statusCode: 403, |
|
||||
statusMessage: 'Account is disabled', |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
if (url.pathname.startsWith('/api/admin')) { |
|
||||
if (user.role !== 'ADMIN') { |
|
||||
throw createError({ |
|
||||
statusCode: 403, |
|
||||
statusMessage: 'Missing Permissions', |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
}); |
|
@ -1,13 +1,13 @@ |
|||||
export default defineEventHandler(async () => { |
export default defineEventHandler(async () => { |
||||
// TODO: check password
|
// TODO: check password
|
||||
|
|
||||
const system = await Database.system.get(); |
const prometheus = await Database.metrics.prometheus.get('wg0'); |
||||
if (!system.metrics.prometheus.enabled) { |
if (!prometheus) { |
||||
throw createError({ |
throw createError({ |
||||
statusCode: 400, |
statusCode: 400, |
||||
message: 'Prometheus metrics are not enabled', |
message: 'Prometheus metrics are not enabled', |
||||
}); |
}); |
||||
} |
} |
||||
|
|
||||
return WireGuard.getMetricsJSON(); |
return getMetricsJSON(); |
||||
}); |
}); |
||||
|
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue