"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.fromConfigFile = exports.WireguardSetConfigResolver = exports.WireguardSetConfigRequest = exports.WireguardIniConfig = exports.WireguardGetConfigResponse = exports.WireguardGetConfigResolver = exports.WireguardGetConfigRequest = exports.WireguardConfig = void 0;
var FileSystem = _interopRequireWildcard(require("@effect/platform/FileSystem"));
var Path = _interopRequireWildcard(require("@effect/platform/Path"));
var Array = _interopRequireWildcard(require("effect/Array"));
var Effect = _interopRequireWildcard(require("effect/Effect"));
var Either = _interopRequireWildcard(require("effect/Either"));
var Function = _interopRequireWildcard(require("effect/Function"));
var Number = _interopRequireWildcard(require("effect/Number"));
var Option = _interopRequireWildcard(require("effect/Option"));
var ParseResult = _interopRequireWildcard(require("effect/ParseResult"));
var Predicate = _interopRequireWildcard(require("effect/Predicate"));
var Request = _interopRequireWildcard(require("effect/Request"));
var Resolver = _interopRequireWildcard(require("effect/RequestResolver"));
var Schema = _interopRequireWildcard(require("effect/Schema"));
var String = _interopRequireWildcard(require("effect/String"));
var ini = _interopRequireWildcard(require("ini"));
var InternetSchemas = _interopRequireWildcard(require("./InternetSchemas.js"));
var WireguardControl = _interopRequireWildcard(require("./WireguardControl.js"));
var WireguardErrors = _interopRequireWildcard(require("./WireguardErrors.js"));
var WireguardInterface = _interopRequireWildcard(require("./WireguardInterface.js"));
var WireguardKey = _interopRequireWildcard(require("./WireguardKey.js"));
var WireguardPeer = _interopRequireWildcard(require("./WireguardPeer.js"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
/**
 * Wireguard config schema definitions
 *
 * @since 1.0.0
 */

/**
 * A wireguard configuration.
 *
 * @since 1.0.0
 * @category Schemas
 */
class WireguardConfig extends /*#__PURE__*/Schema.Class("WireguardIniConfig")({
  /** The Address of this peer. */
  Address: InternetSchemas.CidrBlockFromString,
  /** DNS for this peer. */
  Dns: /*#__PURE__*/Schema.optional(InternetSchemas.Address),
  /**
   * The value for this is a decimal-string integer corresponding to the
   * listening port of the interface.
   */
  ListenPort: /*#__PURE__*/Schema.Union(InternetSchemas.Port, /*#__PURE__*/Schema.transformOrFail(Schema.String, InternetSchemas.Port, {
    decode: (str, _options, ast) => Either.fromOption(Number.parse(str), () => new ParseResult.Type(ast, str)),
    encode: port => Effect.succeed(`${port}`)
  })),
  /**
   * The value for this is a decimal-string integer corresponding to the
   * fwmark of the interface. The value may 0 in the case of a set operation,
   * in which case it indicates that the fwmark should be removed.
   */
  FirewallMark: /*#__PURE__*/Schema.optionalWith(Schema.Number, {
    nullable: true
  }),
  /**
   * The value for this key should be a lowercase hex-encoded private key of
   * the interface. The value may be an all zero string in the case of a set
   * operation, in which case it indicates that the private key should be
   * removed.
   */
  PrivateKey: WireguardKey.WireguardKey,
  /** List of peers to add. */
  Peers: /*#__PURE__*/Schema.optionalWith(/*#__PURE__*/Schema.Array(WireguardPeer.WireguardPeer), {
    default: () => [],
    nullable: true
  })
}) {
  /**
   * Writes a wireguard interface configuration to an INI file.
   *
   * @since 1.0.0
   * @category Constructors
   * @param file - The path to the INI file.
   */
  writeToFile = file => Effect.gen(this, function* () {
    const path = yield* Path.Path;
    const fs = yield* FileSystem.FileSystem;
    const configEncoded = yield* Schema.encode(WireguardConfig)(this);
    const iniConfigDecoded = yield* Schema.decode(WireguardIniConfig)(configEncoded);
    yield* fs.makeDirectory(path.dirname(file), {
      recursive: true
    });
    yield* fs.writeFileString(file, iniConfigDecoded);
  });
  /**
   * Starts a wireguard tunnel that will continue to run and serve traffic
   * even after the nodejs process exits.
   *
   * @since 1.0.0
   * @category Wireguard
   */
  up = interfaceObject => Effect.gen(this, function* () {
    const io = yield* Function.pipe(interfaceObject, Option.fromNullable, Option.map(Effect.succeed), Option.getOrElse(() => WireguardInterface.WireguardInterface.getNextAvailableInterface));
    const wireguardControl = yield* WireguardControl.WireguardControl;
    return yield* wireguardControl.up(this, io);
  });
  /**
   * Starts a wireguard tunnel that will be gracefully shutdown and stop
   * serving traffic once the scope is closed.
   *
   * @since 1.0.0
   * @category Wireguard
   */
  upScoped = interfaceObject => Effect.gen(this, function* () {
    const io = yield* Function.pipe(interfaceObject, Option.fromNullable, Option.map(Effect.succeed), Option.getOrElse(() => WireguardInterface.WireguardInterface.getNextAvailableInterface));
    const wireguardControl = yield* WireguardControl.WireguardControl;
    return yield* wireguardControl.upScoped(this, io);
  });
}
/**
 * A wireguard configuration encoded in the INI format.
 *
 * @since 1.0.0
 * @category Schema Transformations
 * @see {@link WireguardConfig}
 */
exports.WireguardConfig = WireguardConfig;
const WireguardIniConfig = exports.WireguardIniConfig = /*#__PURE__*/Schema.transformOrFail(WireguardConfig, Schema.String, {
  // Encoding is non-trivial, as we need to handle all the peers individually.
  decode: (config, _options, _ast) => Effect.gen(function* () {
    const listenPort = `ListenPort = ${config.ListenPort}\n`;
    const privateKey = `PrivateKey = ${config.PrivateKey}\n`;
    const address = `Address = ${config.Address.address.ip}/${config.Address.mask}\n`;
    const dns = Predicate.isNotUndefined(config.Dns) ? `Dns = ${config.Dns?.ip}\n` : "";
    const fwmark = Predicate.isNotUndefined(config.FirewallMark) ? `FirewallMark = ${config.FirewallMark}\n` : "";
    const peersConfig = yield* Function.pipe(config.Peers, Array.map(peer => Schema.encode(WireguardPeer.WireguardPeer)(peer)), Array.map(Effect.flatMap(Schema.decode(WireguardPeer.WireguardIniPeer))), Effect.allWith(), Effect.map(Array.join("\n")));
    return `[Interface]\n${dns}${listenPort}${fwmark}${address}${privateKey}\n${peersConfig}`;
  }).pipe(Effect.mapError(({
    issue
  }) => issue)),
  // Decoding is likewise non-trivial, as we need to parse all the peers from the ini config.
  encode: (iniConfig, _options, _ast) => Effect.gen(function* () {
    const sections = iniConfig.split(/(?=\[Peer\])/g);
    const maybeInterfaceSection = Array.findFirst(sections, text => text.startsWith("[Interface]"));
    const interfaceSection = Option.getOrThrowWith(maybeInterfaceSection, () => new WireguardErrors.WireguardError({
      message: "No [Interface] section found"
    }));
    const peerSections = Function.pipe(sections, Array.filter(text => text.startsWith("[Peer]")), Array.map(text => text.replace("[Peer]", "")));
    const parsePeers = yield* Function.pipe(peerSections, Array.map(peer => Schema.encode(WireguardPeer.WireguardIniPeer)(peer)), Effect.allWith());
    const parseInterface = Function.pipe(interfaceSection, ini.parse, jsonConfig => ({
      ...jsonConfig["Interface"],
      Peers: parsePeers
    }), ({
      Address,
      Dns,
      FirewallMark,
      ListenPort,
      Peers,
      PrivateKey
    }) => ({
      Dns,
      Peers,
      Address,
      PrivateKey,
      ListenPort,
      FirewallMark: Number.parse(FirewallMark || "").pipe(Option.getOrUndefined)
    }), Schema.decode(WireguardConfig));
    return yield* parseInterface;
  }).pipe(Effect.mapError(({
    issue
  }) => issue))
}).annotations({
  identifier: "WireguardIniConfig",
  description: "A wireguard ini configuration"
});
/**
 * Loads a wireguard interface configuration from an INI file.
 *
 * @since 1.0.0
 * @category Constructors
 * @param file - The path to the INI file.
 */
const fromConfigFile = file => Effect.gen(function* () {
  const fs = yield* FileSystem.FileSystem;
  const fsConfig = yield* fs.readFileString(file);
  const iniConfigEncoded = yield* Schema.encode(WireguardIniConfig)(fsConfig);
  const config = yield* Schema.decode(WireguardConfig)(iniConfigEncoded);
  return config;
});
/**
 * @since 1.0.0
 * @category Responses
 * @see https://www.wireguard.com/xplatform/
 */
exports.fromConfigFile = fromConfigFile;
const WireguardGetConfigResponse = exports.WireguardGetConfigResponse = /*#__PURE__*/Function.pipe(WireguardConfig, Schema.omit("Peers"), Schema.extend(Schema.Struct({
  Peers: Schema.optionalWith(Schema.Array(WireguardPeer.WireguardUApiGetPeerResponse), {
    default: () => [],
    nullable: true
  })
}))).annotations({
  identifier: "WireguardGetConfigResponse",
  description: "The response of a WireguardGetConfigRequest"
});
/**
 * @since 1.0.0
 * @category Requests
 */
class WireguardGetConfigRequest extends /*#__PURE__*/Request.TaggedClass("WireguardGetConfigRequest") {}
/**
 * @since 1.0.0
 * @category Requests
 */
exports.WireguardGetConfigRequest = WireguardGetConfigRequest;
class WireguardSetConfigRequest extends /*#__PURE__*/Request.TaggedClass("WireguardSetConfigRequest") {}
/**
 * @since 1.0.0
 * @category Resolvers
 */
exports.WireguardSetConfigRequest = WireguardSetConfigRequest;
const WireguardGetConfigResolver = exports.WireguardGetConfigResolver = /*#__PURE__*/Resolver.fromEffect(({
  address,
  wireguardInterface
}) => Effect.gen(function* () {
  const uapiConfig = yield* WireguardControl.userspaceContact(wireguardInterface, "get=1\n\n");
  const [interfaceConfig, ...peers] = uapiConfig.split("public_key=");
  const {
    fwmark,
    listen_port,
    private_key
  } = ini.decode(interfaceConfig);
  const peerConfigs = yield* Function.pipe(peers, Array.map(peer => `public_key=${peer}`), Array.map(WireguardPeer.parseWireguardUApiGetPeerResponse), Array.map(Effect.flatMap(Schema.encode(WireguardPeer.WireguardUApiGetPeerResponse))), Effect.allWith());
  return yield* Schema.decode(WireguardGetConfigResponse)({
    Address: address,
    ListenPort: listen_port,
    PrivateKey: Buffer.from(private_key, "hex").toString("base64"),
    FirewallMark: Number.parse(fwmark || "").pipe(Option.getOrUndefined),
    Peers: peerConfigs
  });
}));
/**
 * @since 1.0.0
 * @category Resolvers
 */
const WireguardSetConfigResolver = exports.WireguardSetConfigResolver = /*#__PURE__*/Resolver.fromEffect(({
  config,
  wireguardInterface
}) => Effect.gen(function* () {
  const listenPort = `listen_port=${config.ListenPort}\n`;
  const privateKeyHex = Buffer.from(config.PrivateKey, "base64").toString("hex");
  const privateKey = `private_key=${privateKeyHex}\n`;
  const fwmark = Predicate.isNotUndefined(config.FirewallMark) ? `fwmark=${config.FirewallMark}\n` : String.empty;
  const peers = Function.pipe(config.Peers, Array.map(WireguardPeer.makeWireguardUApiSetPeerRequest), Array.join("\n"));
  const uapiConfig = `${privateKey}${listenPort}${fwmark}${peers}\n`;
  yield* WireguardControl.userspaceContact(wireguardInterface, `set=1\n${uapiConfig}\n`);
  return wireguardInterface;
}));
//# sourceMappingURL=WireguardConfig.js.map