mirror of https://github.com/wg-easy/wg-easy
20 changed files with 227 additions and 199 deletions
@ -1,8 +1,11 @@ |
|||||
|
import { ClientCreateSchema } from '#db/repositories/client/types'; |
||||
|
|
||||
export default defineEventHandler(async (event) => { |
export default defineEventHandler(async (event) => { |
||||
const { name, expireDate } = await readValidatedBody( |
const { name, expiresAt } = await readValidatedBody( |
||||
event, |
event, |
||||
validateZod(createType) |
validateZod(ClientCreateSchema) |
||||
); |
); |
||||
await WireGuard.createClient({ name, expireDate }); |
await Database.clients.create({ name, expiresAt }); |
||||
|
await WireGuard.saveConfig(); |
||||
return { success: true }; |
return { success: true }; |
||||
}); |
}); |
||||
|
@ -0,0 +1,106 @@ |
|||||
|
import type { DBType } from '#db/sqlite'; |
||||
|
import { eq, sql } from 'drizzle-orm'; |
||||
|
import { client } from './schema'; |
||||
|
import type { ClientCreateType } from './types'; |
||||
|
import { wgInterface, userConfig } from '../../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(), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
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.all(); |
||||
|
return result.map((row) => ({ |
||||
|
...row, |
||||
|
createdAt: new Date(row.createdAt), |
||||
|
updatedAt: new Date(row.updatedAt), |
||||
|
})); |
||||
|
} |
||||
|
|
||||
|
async get(id: number) { |
||||
|
return this.#statements.findById.all({ 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(); |
||||
|
} |
||||
|
|
||||
|
await 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); |
||||
|
|
||||
|
return 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(); |
||||
|
}); |
||||
|
} |
||||
|
} |
@ -1,39 +0,0 @@ |
|||||
import type { DBType } from '#db/sqlite'; |
|
||||
import { eq, sql } from 'drizzle-orm'; |
|
||||
import { clients } from './schema'; |
|
||||
|
|
||||
function createPreparedStatement(db: DBType) { |
|
||||
return { |
|
||||
findAll: db.query.clients |
|
||||
.findMany({ |
|
||||
with: { |
|
||||
oneTimeLink: true, |
|
||||
}, |
|
||||
}) |
|
||||
.prepare(), |
|
||||
findById: db.query.clients |
|
||||
.findFirst({ where: eq(clients.id, sql.placeholder('id')) }) |
|
||||
.prepare(), |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
export class ClientsService { |
|
||||
#statements: ReturnType<typeof createPreparedStatement>; |
|
||||
|
|
||||
constructor(db: DBType) { |
|
||||
this.#statements = createPreparedStatement(db); |
|
||||
} |
|
||||
|
|
||||
async findAll() { |
|
||||
const result = await this.#statements.findAll.all(); |
|
||||
return result.map((row) => ({ |
|
||||
...row, |
|
||||
createdAt: new Date(row.createdAt), |
|
||||
updatedAt: new Date(row.updatedAt), |
|
||||
})); |
|
||||
} |
|
||||
|
|
||||
async findById(id: number) { |
|
||||
return this.#statements.findById.all({ id }); |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,4 @@ |
|||||
|
import type { InferSelectModel } from 'drizzle-orm'; |
||||
|
import type { hooks } from './schema'; |
||||
|
|
||||
|
export type HooksType = InferSelectModel<typeof hooks>; |
@ -0,0 +1,4 @@ |
|||||
|
import type { InferSelectModel } from 'drizzle-orm'; |
||||
|
import type { wgInterface } from './schema'; |
||||
|
|
||||
|
export type InterfaceType = InferSelectModel<typeof wgInterface>; |
@ -1,7 +1,7 @@ |
|||||
import { sql } from 'drizzle-orm'; |
import { sql } from 'drizzle-orm'; |
||||
import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core'; |
import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core'; |
||||
|
|
||||
export const users = sqliteTable('users_table', { |
export const user = sqliteTable('users_table', { |
||||
id: int().primaryKey({ autoIncrement: true }), |
id: int().primaryKey({ autoIncrement: true }), |
||||
username: text().notNull(), |
username: text().notNull(), |
||||
password: text().notNull(), |
password: text().notNull(), |
@ -0,0 +1,4 @@ |
|||||
|
import type { InferSelectModel } from 'drizzle-orm'; |
||||
|
import type { userConfig } from './schema'; |
||||
|
|
||||
|
export type UserConfigType = InferSelectModel<typeof userConfig>; |
@ -1,9 +1,9 @@ |
|||||
// Make sure to not use any Path Aliases in these files
|
// Make sure to not use any Path Aliases in these files
|
||||
export * from './repositories/clients/schema'; |
export * from './repositories/client/schema'; |
||||
export * from './repositories/general/schema'; |
export * from './repositories/general/schema'; |
||||
export * from './repositories/hooks/schema'; |
export * from './repositories/hooks/schema'; |
||||
export * from './repositories/interface/schema'; |
export * from './repositories/interface/schema'; |
||||
export * from './repositories/metrics/schema'; |
export * from './repositories/metrics/schema'; |
||||
export * from './repositories/oneTimeLinks/schema'; |
export * from './repositories/oneTimeLink/schema'; |
||||
export * from './repositories/userConfig/schema'; |
export * from './repositories/userConfig/schema'; |
||||
export * from './repositories/users/schema'; |
export * from './repositories/user/schema'; |
||||
|
Loading…
Reference in new issue