mirror of
https://github.com/trailbaseio/trailbase.git
synced 2026-01-06 01:40:12 -06:00
Wire up GeoLite2-City geoip databases into dashboard. #64
This commit is contained in:
899
examples/local-first/traildepot/trailbase.d.ts
vendored
Normal file
899
examples/local-first/traildepot/trailbase.d.ts
vendored
Normal file
@@ -0,0 +1,899 @@
|
||||
export declare function addCronCallback(name: string, schedule: string, cb: () => void | Promise<void>): void;
|
||||
|
||||
export declare function addPeriodicCallback(milliseconds: number, cb: (cancel: () => void) => void): () => void;
|
||||
|
||||
export declare function addRoute(method: Method, route: string, callback: CallbackType): void;
|
||||
|
||||
export declare type CallbackType = (req: RequestType) => MaybeResponse<ResponseType_2>;
|
||||
|
||||
declare namespace Deno_2 {
|
||||
interface ReadFileOptions {
|
||||
/**
|
||||
* An abort signal to allow cancellation of the file read operation.
|
||||
* If the signal becomes aborted the readFile operation will be stopped
|
||||
* and the promise returned will be rejected with an AbortError.
|
||||
*/
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
interface WriteFileOptions {
|
||||
/** If set to `true`, will append to a file instead of overwriting previous
|
||||
* contents.
|
||||
*
|
||||
* @default {false} */
|
||||
append?: boolean;
|
||||
/** Sets the option to allow creating a new file, if one doesn't already
|
||||
* exist at the specified path.
|
||||
*
|
||||
* @default {true} */
|
||||
create?: boolean;
|
||||
/** If set to `true`, no file, directory, or symlink is allowed to exist at
|
||||
* the target location. When createNew is set to `true`, `create` is ignored.
|
||||
*
|
||||
* @default {false} */
|
||||
createNew?: boolean;
|
||||
/** Permissions always applied to file. */
|
||||
mode?: number;
|
||||
/** An abort signal to allow cancellation of the file write operation.
|
||||
*
|
||||
* If the signal becomes aborted the write file operation will be stopped
|
||||
* and the promise returned will be rejected with an {@linkcode AbortError}.
|
||||
*/
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
/**
|
||||
* Options which can be set when using {@linkcode Deno.makeTempDir},
|
||||
* {@linkcode Deno.makeTempDirSync}, {@linkcode Deno.makeTempFile}, and
|
||||
* {@linkcode Deno.makeTempFileSync}.
|
||||
*
|
||||
* @category File System */
|
||||
interface MakeTempOptions {
|
||||
/** Directory where the temporary directory should be created (defaults to
|
||||
* the env variable `TMPDIR`, or the system's default, usually `/tmp`).
|
||||
*
|
||||
* Note that if the passed `dir` is relative, the path returned by
|
||||
* `makeTempFile()` and `makeTempDir()` will also be relative. Be mindful of
|
||||
* this when changing working directory. */
|
||||
dir?: string;
|
||||
/** String that should precede the random portion of the temporary
|
||||
* directory's name. */
|
||||
prefix?: string;
|
||||
/** String that should follow the random portion of the temporary
|
||||
* directory's name. */
|
||||
suffix?: string;
|
||||
}
|
||||
/**
|
||||
* Options which can be set when using {@linkcode Deno.mkdir} and
|
||||
* {@linkcode Deno.mkdirSync}.
|
||||
*
|
||||
* @category File System */
|
||||
interface MkdirOptions {
|
||||
/** If set to `true`, means that any intermediate directories will also be
|
||||
* created (as with the shell command `mkdir -p`).
|
||||
*
|
||||
* Intermediate directories are created with the same permissions.
|
||||
*
|
||||
* When recursive is set to `true`, succeeds silently (without changing any
|
||||
* permissions) if a directory already exists at the path, or if the path
|
||||
* is a symlink to an existing directory.
|
||||
*
|
||||
* @default {false} */
|
||||
recursive?: boolean;
|
||||
/** Permissions to use when creating the directory (defaults to `0o777`,
|
||||
* before the process's umask).
|
||||
*
|
||||
* Ignored on Windows. */
|
||||
mode?: number;
|
||||
}
|
||||
/**
|
||||
* Information about a directory entry returned from {@linkcode Deno.readDir}
|
||||
* and {@linkcode Deno.readDirSync}.
|
||||
*
|
||||
* @category File System */
|
||||
interface DirEntry {
|
||||
/** The file name of the entry. It is just the entity name and does not
|
||||
* include the full path. */
|
||||
name: string;
|
||||
/** True if this is info for a regular file. Mutually exclusive to
|
||||
* `DirEntry.isDirectory` and `DirEntry.isSymlink`. */
|
||||
isFile: boolean;
|
||||
/** True if this is info for a regular directory. Mutually exclusive to
|
||||
* `DirEntry.isFile` and `DirEntry.isSymlink`. */
|
||||
isDirectory: boolean;
|
||||
/** True if this is info for a symlink. Mutually exclusive to
|
||||
* `DirEntry.isFile` and `DirEntry.isDirectory`. */
|
||||
isSymlink: boolean;
|
||||
}
|
||||
/**
|
||||
* Options which can be set when doing {@linkcode Deno.open} and
|
||||
* {@linkcode Deno.openSync}.
|
||||
*
|
||||
* @category File System */
|
||||
interface OpenOptions {
|
||||
/** Sets the option for read access. This option, when `true`, means that
|
||||
* the file should be read-able if opened.
|
||||
*
|
||||
* @default {true} */
|
||||
read?: boolean;
|
||||
/** Sets the option for write access. This option, when `true`, means that
|
||||
* the file should be write-able if opened. If the file already exists,
|
||||
* any write calls on it will overwrite its contents, by default without
|
||||
* truncating it.
|
||||
*
|
||||
* @default {false} */
|
||||
write?: boolean;
|
||||
/** Sets the option for the append mode. This option, when `true`, means
|
||||
* that writes will append to a file instead of overwriting previous
|
||||
* contents.
|
||||
*
|
||||
* Note that setting `{ write: true, append: true }` has the same effect as
|
||||
* setting only `{ append: true }`.
|
||||
*
|
||||
* @default {false} */
|
||||
append?: boolean;
|
||||
/** Sets the option for truncating a previous file. If a file is
|
||||
* successfully opened with this option set it will truncate the file to `0`
|
||||
* size if it already exists. The file must be opened with write access
|
||||
* for truncate to work.
|
||||
*
|
||||
* @default {false} */
|
||||
truncate?: boolean;
|
||||
/** Sets the option to allow creating a new file, if one doesn't already
|
||||
* exist at the specified path. Requires write or append access to be
|
||||
* used.
|
||||
*
|
||||
* @default {false} */
|
||||
create?: boolean;
|
||||
/** If set to `true`, no file, directory, or symlink is allowed to exist at
|
||||
* the target location. Requires write or append access to be used. When
|
||||
* createNew is set to `true`, create and truncate are ignored.
|
||||
*
|
||||
* @default {false} */
|
||||
createNew?: boolean;
|
||||
/** Permissions to use if creating the file (defaults to `0o666`, before
|
||||
* the process's umask).
|
||||
*
|
||||
* Ignored on Windows. */
|
||||
mode?: number;
|
||||
}
|
||||
/**
|
||||
* Options which can be set when using {@linkcode Deno.remove} and
|
||||
* {@linkcode Deno.removeSync}.
|
||||
*
|
||||
* @category File System */
|
||||
interface RemoveOptions {
|
||||
/** If set to `true`, path will be removed even if it's a non-empty directory.
|
||||
*
|
||||
* @default {false} */
|
||||
recursive?: boolean;
|
||||
}
|
||||
/** Options that can be used with {@linkcode symlink} and
|
||||
* {@linkcode symlinkSync}.
|
||||
*
|
||||
* @category File System */
|
||||
interface SymlinkOptions {
|
||||
/** Specify the symbolic link type as file, directory or NTFS junction. This
|
||||
* option only applies to Windows and is ignored on other operating systems. */
|
||||
type: "file" | "dir" | "junction";
|
||||
}
|
||||
function writeFile(path: string | URL, data: Uint8Array | ReadableStream<Uint8Array>, options?: WriteFileOptions): Promise<void>;
|
||||
function writeTextFile(path: string | URL, data: string | ReadableStream<string>, options?: WriteFileOptions): Promise<void>;
|
||||
function readTextFile(path: string | URL, options?: ReadFileOptions): Promise<string>;
|
||||
function readFile(path: string | URL, options?: ReadFileOptions): Promise<Uint8Array>;
|
||||
function chmod(path: string | URL, mode: number): Promise<void>;
|
||||
function chown(path: string | URL, uid: number | null, gid: number | null): Promise<void>;
|
||||
function cwd(): string;
|
||||
function makeTempDir(options?: MakeTempOptions): Promise<string>;
|
||||
function makeTempFile(options?: MakeTempOptions): Promise<string>;
|
||||
function mkdir(path: string | URL, options?: MkdirOptions): Promise<void>;
|
||||
function chdir(directory: string | URL): void;
|
||||
function copyFile(fromPath: string | URL, toPath: string | URL): Promise<void>;
|
||||
function readDir(path: string | URL): AsyncIterable<DirEntry>;
|
||||
function readLink(path: string | URL): Promise<string>;
|
||||
function realPath(path: string | URL): Promise<string>;
|
||||
function remove(path: string | URL, options?: RemoveOptions): Promise<void>;
|
||||
function rename(oldpath: string | URL, newpath: string | URL): Promise<void>;
|
||||
function stat(path: string | URL): Promise<FileInfo>;
|
||||
function lstat(path: string | URL): Promise<FileInfo>;
|
||||
function truncate(name: string, len?: number): Promise<void>;
|
||||
function open(path: string | URL, options?: OpenOptions): Promise<FsFile>;
|
||||
function create(path: string | URL): Promise<FsFile>;
|
||||
function symlink(oldpath: string | URL, newpath: string | URL, options?: SymlinkOptions): Promise<void>;
|
||||
function link(oldpath: string, newpath: string): Promise<void>;
|
||||
function utime(path: string | URL, atime: number | Date, mtime: number | Date): Promise<void>;
|
||||
function umask(mask?: number): number;
|
||||
/** Provides information about a file and is returned by
|
||||
* {@linkcode Deno.stat}, {@linkcode Deno.lstat}, {@linkcode Deno.statSync},
|
||||
* and {@linkcode Deno.lstatSync} or from calling `stat()` and `statSync()`
|
||||
* on an {@linkcode Deno.FsFile} instance.
|
||||
*
|
||||
* @category File System
|
||||
*/
|
||||
interface FileInfo {
|
||||
/** True if this is info for a regular file. Mutually exclusive to
|
||||
* `FileInfo.isDirectory` and `FileInfo.isSymlink`. */
|
||||
isFile: boolean;
|
||||
/** True if this is info for a regular directory. Mutually exclusive to
|
||||
* `FileInfo.isFile` and `FileInfo.isSymlink`. */
|
||||
isDirectory: boolean;
|
||||
/** True if this is info for a symlink. Mutually exclusive to
|
||||
* `FileInfo.isFile` and `FileInfo.isDirectory`. */
|
||||
isSymlink: boolean;
|
||||
/** The size of the file, in bytes. */
|
||||
size: number;
|
||||
/** The last modification time of the file. This corresponds to the `mtime`
|
||||
* field from `stat` on Linux/Mac OS and `ftLastWriteTime` on Windows. This
|
||||
* may not be available on all platforms. */
|
||||
mtime: Date | null;
|
||||
/** The last access time of the file. This corresponds to the `atime`
|
||||
* field from `stat` on Unix and `ftLastAccessTime` on Windows. This may not
|
||||
* be available on all platforms. */
|
||||
atime: Date | null;
|
||||
/** The creation time of the file. This corresponds to the `birthtime`
|
||||
* field from `stat` on Mac/BSD and `ftCreationTime` on Windows. This may
|
||||
* not be available on all platforms. */
|
||||
birthtime: Date | null;
|
||||
/** The last change time of the file. This corresponds to the `ctime`
|
||||
* field from `stat` on Mac/BSD and `ChangeTime` on Windows. This may
|
||||
* not be available on all platforms. */
|
||||
ctime: Date | null;
|
||||
/** ID of the device containing the file. */
|
||||
dev: number;
|
||||
/** Inode number.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
ino: number | null;
|
||||
/** The underlying raw `st_mode` bits that contain the standard Unix
|
||||
* permissions for this file/directory.
|
||||
*/
|
||||
mode: number | null;
|
||||
/** Number of hard links pointing to this file.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
nlink: number | null;
|
||||
/** User ID of the owner of this file.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
uid: number | null;
|
||||
/** Group ID of the owner of this file.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
gid: number | null;
|
||||
/** Device ID of this file.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
rdev: number | null;
|
||||
/** Blocksize for filesystem I/O.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
blksize: number | null;
|
||||
/** Number of blocks allocated to the file, in 512-byte units.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
blocks: number | null;
|
||||
/** True if this is info for a block device.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
isBlockDevice: boolean | null;
|
||||
/** True if this is info for a char device.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
isCharDevice: boolean | null;
|
||||
/** True if this is info for a fifo.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
isFifo: boolean | null;
|
||||
/** True if this is info for a socket.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
isSocket: boolean | null;
|
||||
}
|
||||
/**
|
||||
* A enum which defines the seek mode for IO related APIs that support
|
||||
* seeking.
|
||||
*
|
||||
* @category I/O */
|
||||
enum SeekMode {
|
||||
Start = 0,
|
||||
Current = 1,
|
||||
End = 2
|
||||
}
|
||||
/** @category I/O */
|
||||
interface SetRawOptions {
|
||||
/**
|
||||
* The `cbreak` option can be used to indicate that characters that
|
||||
* correspond to a signal should still be generated. When disabling raw
|
||||
* mode, this option is ignored. This functionality currently only works on
|
||||
* Linux and Mac OS.
|
||||
*/
|
||||
cbreak: boolean;
|
||||
}
|
||||
class FsFile implements Disposable {
|
||||
/** A {@linkcode ReadableStream} instance representing to the byte contents
|
||||
* of the file. This makes it easy to interoperate with other web streams
|
||||
* based APIs.
|
||||
*
|
||||
* ```ts
|
||||
* using file = await Deno.open("my_file.txt", { read: true });
|
||||
* const decoder = new TextDecoder();
|
||||
* for await (const chunk of file.readable) {
|
||||
* console.log(decoder.decode(chunk));
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
readonly readable: ReadableStream<Uint8Array>;
|
||||
/** A {@linkcode WritableStream} instance to write the contents of the
|
||||
* file. This makes it easy to interoperate with other web streams based
|
||||
* APIs.
|
||||
*
|
||||
* ```ts
|
||||
* const items = ["hello", "world"];
|
||||
* using file = await Deno.open("my_file.txt", { write: true });
|
||||
* const encoder = new TextEncoder();
|
||||
* const writer = file.writable.getWriter();
|
||||
* for (const item of items) {
|
||||
* await writer.write(encoder.encode(item));
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
readonly writable: WritableStream<Uint8Array>;
|
||||
/** Write the contents of the array buffer (`p`) to the file.
|
||||
*
|
||||
* Resolves to the number of bytes written.
|
||||
*
|
||||
* **It is not guaranteed that the full buffer will be written in a single
|
||||
* call.**
|
||||
*
|
||||
* ```ts
|
||||
* const encoder = new TextEncoder();
|
||||
* const data = encoder.encode("Hello world");
|
||||
* using file = await Deno.open("/foo/bar.txt", { write: true });
|
||||
* const bytesWritten = await file.write(data); // 11
|
||||
* ```
|
||||
*
|
||||
* @category I/O
|
||||
*/
|
||||
write(p: Uint8Array): Promise<number>;
|
||||
/** Synchronously write the contents of the array buffer (`p`) to the file.
|
||||
*
|
||||
* Returns the number of bytes written.
|
||||
*
|
||||
* **It is not guaranteed that the full buffer will be written in a single
|
||||
* call.**
|
||||
*
|
||||
* ```ts
|
||||
* const encoder = new TextEncoder();
|
||||
* const data = encoder.encode("Hello world");
|
||||
* using file = Deno.openSync("/foo/bar.txt", { write: true });
|
||||
* const bytesWritten = file.writeSync(data); // 11
|
||||
* ```
|
||||
*/
|
||||
writeSync(p: Uint8Array): number;
|
||||
/** Truncates (or extends) the file to reach the specified `len`. If `len`
|
||||
* is not specified, then the entire file contents are truncated.
|
||||
*
|
||||
* ### Truncate the entire file
|
||||
*
|
||||
* ```ts
|
||||
* using file = await Deno.open("my_file.txt", { write: true });
|
||||
* await file.truncate();
|
||||
* ```
|
||||
*
|
||||
* ### Truncate part of the file
|
||||
*
|
||||
* ```ts
|
||||
* // if "my_file.txt" contains the text "hello world":
|
||||
* using file = await Deno.open("my_file.txt", { write: true });
|
||||
* await file.truncate(7);
|
||||
* const buf = new Uint8Array(100);
|
||||
* await file.read(buf);
|
||||
* const text = new TextDecoder().decode(buf); // "hello w"
|
||||
* ```
|
||||
*/
|
||||
truncate(len?: number): Promise<void>;
|
||||
/** Synchronously truncates (or extends) the file to reach the specified
|
||||
* `len`. If `len` is not specified, then the entire file contents are
|
||||
* truncated.
|
||||
*
|
||||
* ### Truncate the entire file
|
||||
*
|
||||
* ```ts
|
||||
* using file = Deno.openSync("my_file.txt", { write: true });
|
||||
* file.truncateSync();
|
||||
* ```
|
||||
*
|
||||
* ### Truncate part of the file
|
||||
*
|
||||
* ```ts
|
||||
* // if "my_file.txt" contains the text "hello world":
|
||||
* using file = Deno.openSync("my_file.txt", { write: true });
|
||||
* file.truncateSync(7);
|
||||
* const buf = new Uint8Array(100);
|
||||
* file.readSync(buf);
|
||||
* const text = new TextDecoder().decode(buf); // "hello w"
|
||||
* ```
|
||||
*/
|
||||
truncateSync(len?: number): void;
|
||||
/** Read the file into an array buffer (`p`).
|
||||
*
|
||||
* Resolves to either the number of bytes read during the operation or EOF
|
||||
* (`null`) if there was nothing more to read.
|
||||
*
|
||||
* It is possible for a read to successfully return with `0` bytes. This
|
||||
* does not indicate EOF.
|
||||
*
|
||||
* **It is not guaranteed that the full buffer will be read in a single
|
||||
* call.**
|
||||
*
|
||||
* ```ts
|
||||
* // if "/foo/bar.txt" contains the text "hello world":
|
||||
* using file = await Deno.open("/foo/bar.txt");
|
||||
* const buf = new Uint8Array(100);
|
||||
* const numberOfBytesRead = await file.read(buf); // 11 bytes
|
||||
* const text = new TextDecoder().decode(buf); // "hello world"
|
||||
* ```
|
||||
*/
|
||||
read(p: Uint8Array): Promise<number | null>;
|
||||
/** Synchronously read from the file into an array buffer (`p`).
|
||||
*
|
||||
* Returns either the number of bytes read during the operation or EOF
|
||||
* (`null`) if there was nothing more to read.
|
||||
*
|
||||
* It is possible for a read to successfully return with `0` bytes. This
|
||||
* does not indicate EOF.
|
||||
*
|
||||
* **It is not guaranteed that the full buffer will be read in a single
|
||||
* call.**
|
||||
*
|
||||
* ```ts
|
||||
* // if "/foo/bar.txt" contains the text "hello world":
|
||||
* using file = Deno.openSync("/foo/bar.txt");
|
||||
* const buf = new Uint8Array(100);
|
||||
* const numberOfBytesRead = file.readSync(buf); // 11 bytes
|
||||
* const text = new TextDecoder().decode(buf); // "hello world"
|
||||
* ```
|
||||
*/
|
||||
readSync(p: Uint8Array): number | null;
|
||||
/** Seek to the given `offset` under mode given by `whence`. The call
|
||||
* resolves to the new position within the resource (bytes from the start).
|
||||
*
|
||||
* ```ts
|
||||
* // Given the file contains "Hello world" text, which is 11 bytes long:
|
||||
* using file = await Deno.open(
|
||||
* "hello.txt",
|
||||
* { read: true, write: true, truncate: true, create: true },
|
||||
* );
|
||||
* await file.write(new TextEncoder().encode("Hello world"));
|
||||
*
|
||||
* // advance cursor 6 bytes
|
||||
* const cursorPosition = await file.seek(6, Deno.SeekMode.Start);
|
||||
* console.log(cursorPosition); // 6
|
||||
* const buf = new Uint8Array(100);
|
||||
* await file.read(buf);
|
||||
* console.log(new TextDecoder().decode(buf)); // "world"
|
||||
* ```
|
||||
*
|
||||
* The seek modes work as follows:
|
||||
*
|
||||
* ```ts
|
||||
* // Given the file contains "Hello world" text, which is 11 bytes long:
|
||||
* const file = await Deno.open(
|
||||
* "hello.txt",
|
||||
* { read: true, write: true, truncate: true, create: true },
|
||||
* );
|
||||
* await file.write(new TextEncoder().encode("Hello world"));
|
||||
*
|
||||
* // Seek 6 bytes from the start of the file
|
||||
* console.log(await file.seek(6, Deno.SeekMode.Start)); // "6"
|
||||
* // Seek 2 more bytes from the current position
|
||||
* console.log(await file.seek(2, Deno.SeekMode.Current)); // "8"
|
||||
* // Seek backwards 2 bytes from the end of the file
|
||||
* console.log(await file.seek(-2, Deno.SeekMode.End)); // "9" (i.e. 11-2)
|
||||
* ```
|
||||
*/
|
||||
seek(offset: number | bigint, whence: SeekMode): Promise<number>;
|
||||
/** Synchronously seek to the given `offset` under mode given by `whence`.
|
||||
* The new position within the resource (bytes from the start) is returned.
|
||||
*
|
||||
* ```ts
|
||||
* using file = Deno.openSync(
|
||||
* "hello.txt",
|
||||
* { read: true, write: true, truncate: true, create: true },
|
||||
* );
|
||||
* file.writeSync(new TextEncoder().encode("Hello world"));
|
||||
*
|
||||
* // advance cursor 6 bytes
|
||||
* const cursorPosition = file.seekSync(6, Deno.SeekMode.Start);
|
||||
* console.log(cursorPosition); // 6
|
||||
* const buf = new Uint8Array(100);
|
||||
* file.readSync(buf);
|
||||
* console.log(new TextDecoder().decode(buf)); // "world"
|
||||
* ```
|
||||
*
|
||||
* The seek modes work as follows:
|
||||
*
|
||||
* ```ts
|
||||
* // Given the file contains "Hello world" text, which is 11 bytes long:
|
||||
* using file = Deno.openSync(
|
||||
* "hello.txt",
|
||||
* { read: true, write: true, truncate: true, create: true },
|
||||
* );
|
||||
* file.writeSync(new TextEncoder().encode("Hello world"));
|
||||
*
|
||||
* // Seek 6 bytes from the start of the file
|
||||
* console.log(file.seekSync(6, Deno.SeekMode.Start)); // "6"
|
||||
* // Seek 2 more bytes from the current position
|
||||
* console.log(file.seekSync(2, Deno.SeekMode.Current)); // "8"
|
||||
* // Seek backwards 2 bytes from the end of the file
|
||||
* console.log(file.seekSync(-2, Deno.SeekMode.End)); // "9" (i.e. 11-2)
|
||||
* ```
|
||||
*/
|
||||
seekSync(offset: number | bigint, whence: SeekMode): number;
|
||||
/** Resolves to a {@linkcode Deno.FileInfo} for the file.
|
||||
*
|
||||
* ```ts
|
||||
* import { assert } from "jsr:@std/assert";
|
||||
*
|
||||
* using file = await Deno.open("hello.txt");
|
||||
* const fileInfo = await file.stat();
|
||||
* assert(fileInfo.isFile);
|
||||
* ```
|
||||
*/
|
||||
stat(): Promise<FileInfo>;
|
||||
/** Synchronously returns a {@linkcode Deno.FileInfo} for the file.
|
||||
*
|
||||
* ```ts
|
||||
* import { assert } from "jsr:@std/assert";
|
||||
*
|
||||
* using file = Deno.openSync("hello.txt")
|
||||
* const fileInfo = file.statSync();
|
||||
* assert(fileInfo.isFile);
|
||||
* ```
|
||||
*/
|
||||
statSync(): FileInfo;
|
||||
/**
|
||||
* Flushes any pending data and metadata operations of the given file
|
||||
* stream to disk.
|
||||
*
|
||||
* ```ts
|
||||
* const file = await Deno.open(
|
||||
* "my_file.txt",
|
||||
* { read: true, write: true, create: true },
|
||||
* );
|
||||
* await file.write(new TextEncoder().encode("Hello World"));
|
||||
* await file.truncate(1);
|
||||
* await file.sync();
|
||||
* console.log(await Deno.readTextFile("my_file.txt")); // H
|
||||
* ```
|
||||
*
|
||||
* @category I/O
|
||||
*/
|
||||
sync(): Promise<void>;
|
||||
/**
|
||||
* Synchronously flushes any pending data and metadata operations of the given
|
||||
* file stream to disk.
|
||||
*
|
||||
* ```ts
|
||||
* const file = Deno.openSync(
|
||||
* "my_file.txt",
|
||||
* { read: true, write: true, create: true },
|
||||
* );
|
||||
* file.writeSync(new TextEncoder().encode("Hello World"));
|
||||
* file.truncateSync(1);
|
||||
* file.syncSync();
|
||||
* console.log(Deno.readTextFileSync("my_file.txt")); // H
|
||||
* ```
|
||||
*
|
||||
* @category I/O
|
||||
*/
|
||||
syncSync(): void;
|
||||
/**
|
||||
* Flushes any pending data operations of the given file stream to disk.
|
||||
* ```ts
|
||||
* using file = await Deno.open(
|
||||
* "my_file.txt",
|
||||
* { read: true, write: true, create: true },
|
||||
* );
|
||||
* await file.write(new TextEncoder().encode("Hello World"));
|
||||
* await file.syncData();
|
||||
* console.log(await Deno.readTextFile("my_file.txt")); // Hello World
|
||||
* ```
|
||||
*
|
||||
* @category I/O
|
||||
*/
|
||||
syncData(): Promise<void>;
|
||||
/**
|
||||
* Synchronously flushes any pending data operations of the given file stream
|
||||
* to disk.
|
||||
*
|
||||
* ```ts
|
||||
* using file = Deno.openSync(
|
||||
* "my_file.txt",
|
||||
* { read: true, write: true, create: true },
|
||||
* );
|
||||
* file.writeSync(new TextEncoder().encode("Hello World"));
|
||||
* file.syncDataSync();
|
||||
* console.log(Deno.readTextFileSync("my_file.txt")); // Hello World
|
||||
* ```
|
||||
*
|
||||
* @category I/O
|
||||
*/
|
||||
syncDataSync(): void;
|
||||
/**
|
||||
* Changes the access (`atime`) and modification (`mtime`) times of the
|
||||
* file stream resource. Given times are either in seconds (UNIX epoch
|
||||
* time) or as `Date` objects.
|
||||
*
|
||||
* ```ts
|
||||
* using file = await Deno.open("file.txt", { create: true, write: true });
|
||||
* await file.utime(1556495550, new Date());
|
||||
* ```
|
||||
*
|
||||
* @category File System
|
||||
*/
|
||||
utime(atime: number | Date, mtime: number | Date): Promise<void>;
|
||||
/**
|
||||
* Synchronously changes the access (`atime`) and modification (`mtime`)
|
||||
* times of the file stream resource. Given times are either in seconds
|
||||
* (UNIX epoch time) or as `Date` objects.
|
||||
*
|
||||
* ```ts
|
||||
* using file = Deno.openSync("file.txt", { create: true, write: true });
|
||||
* file.utime(1556495550, new Date());
|
||||
* ```
|
||||
*
|
||||
* @category File System
|
||||
*/
|
||||
utimeSync(atime: number | Date, mtime: number | Date): void;
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* Checks if the file resource is a TTY (terminal).
|
||||
*
|
||||
* ```ts
|
||||
* // This example is system and context specific
|
||||
* using file = await Deno.open("/dev/tty6");
|
||||
* file.isTerminal(); // true
|
||||
* ```
|
||||
*/
|
||||
isTerminal(): boolean;
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* Set TTY to be under raw mode or not. In raw mode, characters are read and
|
||||
* returned as is, without being processed. All special processing of
|
||||
* characters by the terminal is disabled, including echoing input
|
||||
* characters. Reading from a TTY device in raw mode is faster than reading
|
||||
* from a TTY device in canonical mode.
|
||||
*
|
||||
* ```ts
|
||||
* using file = await Deno.open("/dev/tty6");
|
||||
* file.setRaw(true, { cbreak: true });
|
||||
* ```
|
||||
*/
|
||||
setRaw(mode: boolean, options?: SetRawOptions): void;
|
||||
/**
|
||||
* Acquire an advisory file-system lock for the file.
|
||||
*
|
||||
* @param [exclusive=false]
|
||||
*/
|
||||
lock(exclusive?: boolean): Promise<void>;
|
||||
/**
|
||||
* Synchronously acquire an advisory file-system lock synchronously for the file.
|
||||
*
|
||||
* @param [exclusive=false]
|
||||
*/
|
||||
lockSync(exclusive?: boolean): void;
|
||||
/**
|
||||
* Release an advisory file-system lock for the file.
|
||||
*/
|
||||
unlock(): Promise<void>;
|
||||
/**
|
||||
* Synchronously release an advisory file-system lock for the file.
|
||||
*/
|
||||
unlockSync(): void;
|
||||
/** Close the file. Closing a file when you are finished with it is
|
||||
* important to avoid leaking resources.
|
||||
*
|
||||
* ```ts
|
||||
* using file = await Deno.open("my_file.txt");
|
||||
* // do work with "file" object
|
||||
* ```
|
||||
*/
|
||||
close(): void;
|
||||
[Symbol.dispose](): void;
|
||||
}
|
||||
}
|
||||
|
||||
export declare function execute(sql: string, params: unknown[]): Promise<number>;
|
||||
|
||||
export declare namespace fs {
|
||||
const writeFile: typeof Deno_2.writeFile;
|
||||
const writeTextFile: typeof Deno_2.writeTextFile;
|
||||
const readTextFile: typeof Deno_2.readTextFile;
|
||||
const readFile: typeof Deno_2.readFile;
|
||||
const chmod: typeof Deno_2.chmod;
|
||||
const chown: typeof Deno_2.chown;
|
||||
const cwd: typeof Deno_2.cwd;
|
||||
const makeTempDir: typeof Deno_2.makeTempDir;
|
||||
const makeTempFile: typeof Deno_2.makeTempFile;
|
||||
const mkdir: typeof Deno_2.mkdir;
|
||||
const chdir: typeof Deno_2.chdir;
|
||||
const copyFile: typeof Deno_2.copyFile;
|
||||
const readDir: typeof Deno_2.readDir;
|
||||
const readLink: typeof Deno_2.readLink;
|
||||
const realPath: typeof Deno_2.realPath;
|
||||
const remove: typeof Deno_2.remove;
|
||||
const rename: typeof Deno_2.rename;
|
||||
const stat: typeof Deno_2.stat;
|
||||
const lstat: typeof Deno_2.lstat;
|
||||
const truncate: typeof Deno_2.truncate;
|
||||
const FsFile: typeof Deno_2.FsFile;
|
||||
const open: typeof Deno_2.open;
|
||||
const create: typeof Deno_2.create;
|
||||
const symlink: typeof Deno_2.symlink;
|
||||
const link: typeof Deno_2.link;
|
||||
const utime: typeof Deno_2.utime;
|
||||
const umask: typeof Deno_2.umask;
|
||||
}
|
||||
|
||||
export declare type HeaderMapType = {
|
||||
[key: string]: string;
|
||||
};
|
||||
|
||||
export declare function htmlHandler(f: (req: StringRequestType) => MaybeResponse<HtmlResponseType | string>): CallbackType;
|
||||
|
||||
export declare type HtmlResponseType = {
|
||||
headers?: [string, string][];
|
||||
status?: number;
|
||||
body: string;
|
||||
};
|
||||
|
||||
export declare class HttpError extends Error {
|
||||
readonly statusCode: number;
|
||||
readonly headers: [string, string][] | undefined;
|
||||
constructor(statusCode: number, message?: string, headers?: [string, string][]);
|
||||
toString(): string;
|
||||
toResponse(): ResponseType_2;
|
||||
}
|
||||
|
||||
export declare function jsonHandler(f: (req: JsonRequestType) => MaybeResponse<JsonRequestType | object>): CallbackType;
|
||||
|
||||
export declare type JsonRequestType = {
|
||||
uri: string;
|
||||
params: PathParamsType;
|
||||
headers: HeaderMapType;
|
||||
user?: UserType;
|
||||
body?: object | string;
|
||||
};
|
||||
|
||||
export declare interface JsonResponseType {
|
||||
headers?: [string, string][];
|
||||
status?: number;
|
||||
body: object;
|
||||
}
|
||||
|
||||
export declare type MaybeResponse<T> = Promise<T | undefined> | T | undefined;
|
||||
|
||||
export declare type Method = "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT" | "TRACE";
|
||||
|
||||
export declare type ParsedPath = {
|
||||
path: string;
|
||||
query: URLSearchParams;
|
||||
};
|
||||
|
||||
export declare function parsePath(path: string): ParsedPath;
|
||||
|
||||
export declare type PathParamsType = {
|
||||
[key: string]: string;
|
||||
};
|
||||
|
||||
export declare function query(sql: string, params: unknown[]): Promise<unknown[][]>;
|
||||
|
||||
export declare type RequestType = {
|
||||
uri: string;
|
||||
params: PathParamsType;
|
||||
headers: HeaderMapType;
|
||||
user?: UserType;
|
||||
body?: Uint8Array;
|
||||
};
|
||||
|
||||
declare type ResponseType_2 = {
|
||||
headers?: [string, string][];
|
||||
status?: number;
|
||||
body?: Uint8Array;
|
||||
};
|
||||
export { ResponseType_2 as ResponseType }
|
||||
|
||||
export declare enum StatusCodes {
|
||||
CONTINUE = 100,
|
||||
SWITCHING_PROTOCOLS = 101,
|
||||
PROCESSING = 102,
|
||||
EARLY_HINTS = 103,
|
||||
OK = 200,
|
||||
CREATED = 201,
|
||||
ACCEPTED = 202,
|
||||
NON_AUTHORITATIVE_INFORMATION = 203,
|
||||
NO_CONTENT = 204,
|
||||
RESET_CONTENT = 205,
|
||||
PARTIAL_CONTENT = 206,
|
||||
MULTI_STATUS = 207,
|
||||
MULTIPLE_CHOICES = 300,
|
||||
MOVED_PERMANENTLY = 301,
|
||||
MOVED_TEMPORARILY = 302,
|
||||
SEE_OTHER = 303,
|
||||
NOT_MODIFIED = 304,
|
||||
USE_PROXY = 305,
|
||||
TEMPORARY_REDIRECT = 307,
|
||||
PERMANENT_REDIRECT = 308,
|
||||
BAD_REQUEST = 400,
|
||||
UNAUTHORIZED = 401,
|
||||
PAYMENT_REQUIRED = 402,
|
||||
FORBIDDEN = 403,
|
||||
NOT_FOUND = 404,
|
||||
METHOD_NOT_ALLOWED = 405,
|
||||
NOT_ACCEPTABLE = 406,
|
||||
PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||
REQUEST_TIMEOUT = 408,
|
||||
CONFLICT = 409,
|
||||
GONE = 410,
|
||||
LENGTH_REQUIRED = 411,
|
||||
PRECONDITION_FAILED = 412,
|
||||
REQUEST_TOO_LONG = 413,
|
||||
REQUEST_URI_TOO_LONG = 414,
|
||||
UNSUPPORTED_MEDIA_TYPE = 415,
|
||||
REQUESTED_RANGE_NOT_SATISFIABLE = 416,
|
||||
EXPECTATION_FAILED = 417,
|
||||
IM_A_TEAPOT = 418,
|
||||
INSUFFICIENT_SPACE_ON_RESOURCE = 419,
|
||||
METHOD_FAILURE = 420,
|
||||
MISDIRECTED_REQUEST = 421,
|
||||
UNPROCESSABLE_ENTITY = 422,
|
||||
LOCKED = 423,
|
||||
FAILED_DEPENDENCY = 424,
|
||||
UPGRADE_REQUIRED = 426,
|
||||
PRECONDITION_REQUIRED = 428,
|
||||
TOO_MANY_REQUESTS = 429,
|
||||
REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
|
||||
UNAVAILABLE_FOR_LEGAL_REASONS = 451,
|
||||
INTERNAL_SERVER_ERROR = 500,
|
||||
NOT_IMPLEMENTED = 501,
|
||||
BAD_GATEWAY = 502,
|
||||
SERVICE_UNAVAILABLE = 503,
|
||||
GATEWAY_TIMEOUT = 504,
|
||||
HTTP_VERSION_NOT_SUPPORTED = 505,
|
||||
INSUFFICIENT_STORAGE = 507,
|
||||
NETWORK_AUTHENTICATION_REQUIRED = 511
|
||||
}
|
||||
|
||||
export declare function stringHandler(f: (req: StringRequestType) => MaybeResponse<StringResponseType | string>): CallbackType;
|
||||
|
||||
export declare type StringRequestType = {
|
||||
uri: string;
|
||||
params: PathParamsType;
|
||||
headers: HeaderMapType;
|
||||
user?: UserType;
|
||||
body?: string;
|
||||
};
|
||||
|
||||
export declare type StringResponseType = {
|
||||
headers?: [string, string][];
|
||||
status?: number;
|
||||
body: string;
|
||||
};
|
||||
|
||||
export declare class Transaction {
|
||||
finalized: boolean;
|
||||
constructor();
|
||||
query(queryStr: string, params: unknown[]): unknown[][];
|
||||
execute(queryStr: string, params: unknown[]): number;
|
||||
commit(): void;
|
||||
rollback(): void;
|
||||
}
|
||||
|
||||
export declare function transaction<T>(f: (tx: Transaction) => T): Promise<T>;
|
||||
|
||||
export declare type UserType = {
|
||||
id: string;
|
||||
email: string;
|
||||
csrf: string;
|
||||
};
|
||||
|
||||
export { }
|
||||
463
examples/local-first/traildepot/trailbase.js
Normal file
463
examples/local-first/traildepot/trailbase.js
Normal file
@@ -0,0 +1,463 @@
|
||||
var __defProp = Object.defineProperty;
|
||||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
||||
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
||||
var fs;
|
||||
((fs2) => {
|
||||
fs2.writeFile = Deno.writeFile;
|
||||
fs2.writeTextFile = Deno.writeTextFile;
|
||||
fs2.readTextFile = Deno.readTextFile;
|
||||
fs2.readFile = Deno.readFile;
|
||||
fs2.chmod = Deno.chmod;
|
||||
fs2.chown = Deno.chown;
|
||||
fs2.cwd = Deno.cwd;
|
||||
fs2.makeTempDir = Deno.makeTempDir;
|
||||
fs2.makeTempFile = Deno.makeTempFile;
|
||||
fs2.mkdir = Deno.mkdir;
|
||||
fs2.chdir = Deno.chdir;
|
||||
fs2.copyFile = Deno.copyFile;
|
||||
fs2.readDir = Deno.readDir;
|
||||
fs2.readLink = Deno.readLink;
|
||||
fs2.realPath = Deno.realPath;
|
||||
fs2.remove = Deno.remove;
|
||||
fs2.rename = Deno.rename;
|
||||
fs2.stat = Deno.stat;
|
||||
fs2.lstat = Deno.lstat;
|
||||
fs2.truncate = Deno.truncate;
|
||||
fs2.FsFile = Deno.FsFile;
|
||||
fs2.open = Deno.open;
|
||||
fs2.create = Deno.create;
|
||||
fs2.symlink = Deno.symlink;
|
||||
fs2.link = Deno.link;
|
||||
fs2.utime = Deno.utime;
|
||||
fs2.umask = Deno.umask;
|
||||
})(fs || (fs = {}));
|
||||
function decodeFallback(bytes) {
|
||||
var inputIndex = 0;
|
||||
var pendingSize = Math.min(256 * 256, bytes.length + 1);
|
||||
var pending = new Uint16Array(pendingSize);
|
||||
var chunks = [];
|
||||
var pendingIndex = 0;
|
||||
for (; ; ) {
|
||||
var more = inputIndex < bytes.length;
|
||||
if (!more || pendingIndex >= pendingSize - 1) {
|
||||
var subarray = pending.subarray(0, pendingIndex);
|
||||
var arraylike = subarray;
|
||||
chunks.push(String.fromCharCode.apply(null, arraylike));
|
||||
if (!more) {
|
||||
return chunks.join("");
|
||||
}
|
||||
bytes = bytes.subarray(inputIndex);
|
||||
inputIndex = 0;
|
||||
pendingIndex = 0;
|
||||
}
|
||||
var byte1 = bytes[inputIndex++];
|
||||
if ((byte1 & 128) === 0) {
|
||||
pending[pendingIndex++] = byte1;
|
||||
} else if ((byte1 & 224) === 192) {
|
||||
var byte2 = bytes[inputIndex++] & 63;
|
||||
pending[pendingIndex++] = (byte1 & 31) << 6 | byte2;
|
||||
} else if ((byte1 & 240) === 224) {
|
||||
var byte2 = bytes[inputIndex++] & 63;
|
||||
var byte3 = bytes[inputIndex++] & 63;
|
||||
pending[pendingIndex++] = (byte1 & 31) << 12 | byte2 << 6 | byte3;
|
||||
} else if ((byte1 & 248) === 240) {
|
||||
var byte2 = bytes[inputIndex++] & 63;
|
||||
var byte3 = bytes[inputIndex++] & 63;
|
||||
var byte4 = bytes[inputIndex++] & 63;
|
||||
var codepoint = (byte1 & 7) << 18 | byte2 << 12 | byte3 << 6 | byte4;
|
||||
if (codepoint > 65535) {
|
||||
codepoint -= 65536;
|
||||
pending[pendingIndex++] = codepoint >>> 10 & 1023 | 55296;
|
||||
codepoint = 56320 | codepoint & 1023;
|
||||
}
|
||||
pending[pendingIndex++] = codepoint;
|
||||
} else ;
|
||||
}
|
||||
}
|
||||
function encodeFallback(string) {
|
||||
var pos = 0;
|
||||
var len = string.length;
|
||||
var at = 0;
|
||||
var tlen = Math.max(32, len + (len >>> 1) + 7);
|
||||
var target = new Uint8Array(tlen >>> 3 << 3);
|
||||
while (pos < len) {
|
||||
var value = string.charCodeAt(pos++);
|
||||
if (value >= 55296 && value <= 56319) {
|
||||
if (pos < len) {
|
||||
var extra = string.charCodeAt(pos);
|
||||
if ((extra & 64512) === 56320) {
|
||||
++pos;
|
||||
value = ((value & 1023) << 10) + (extra & 1023) + 65536;
|
||||
}
|
||||
}
|
||||
if (value >= 55296 && value <= 56319) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (at + 4 > target.length) {
|
||||
tlen += 8;
|
||||
tlen *= 1 + pos / string.length * 2;
|
||||
tlen = tlen >>> 3 << 3;
|
||||
var update = new Uint8Array(tlen);
|
||||
update.set(target);
|
||||
target = update;
|
||||
}
|
||||
if ((value & 4294967168) === 0) {
|
||||
target[at++] = value;
|
||||
continue;
|
||||
} else if ((value & 4294965248) === 0) {
|
||||
target[at++] = value >>> 6 & 31 | 192;
|
||||
} else if ((value & 4294901760) === 0) {
|
||||
target[at++] = value >>> 12 & 15 | 224;
|
||||
target[at++] = value >>> 6 & 63 | 128;
|
||||
} else if ((value & 4292870144) === 0) {
|
||||
target[at++] = value >>> 18 & 7 | 240;
|
||||
target[at++] = value >>> 12 & 63 | 128;
|
||||
target[at++] = value >>> 6 & 63 | 128;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
target[at++] = value & 63 | 128;
|
||||
}
|
||||
return target.slice ? target.slice(0, at) : target.subarray(0, at);
|
||||
}
|
||||
var StatusCodes = /* @__PURE__ */ ((StatusCodes2) => {
|
||||
StatusCodes2[StatusCodes2["CONTINUE"] = 100] = "CONTINUE";
|
||||
StatusCodes2[StatusCodes2["SWITCHING_PROTOCOLS"] = 101] = "SWITCHING_PROTOCOLS";
|
||||
StatusCodes2[StatusCodes2["PROCESSING"] = 102] = "PROCESSING";
|
||||
StatusCodes2[StatusCodes2["EARLY_HINTS"] = 103] = "EARLY_HINTS";
|
||||
StatusCodes2[StatusCodes2["OK"] = 200] = "OK";
|
||||
StatusCodes2[StatusCodes2["CREATED"] = 201] = "CREATED";
|
||||
StatusCodes2[StatusCodes2["ACCEPTED"] = 202] = "ACCEPTED";
|
||||
StatusCodes2[StatusCodes2["NON_AUTHORITATIVE_INFORMATION"] = 203] = "NON_AUTHORITATIVE_INFORMATION";
|
||||
StatusCodes2[StatusCodes2["NO_CONTENT"] = 204] = "NO_CONTENT";
|
||||
StatusCodes2[StatusCodes2["RESET_CONTENT"] = 205] = "RESET_CONTENT";
|
||||
StatusCodes2[StatusCodes2["PARTIAL_CONTENT"] = 206] = "PARTIAL_CONTENT";
|
||||
StatusCodes2[StatusCodes2["MULTI_STATUS"] = 207] = "MULTI_STATUS";
|
||||
StatusCodes2[StatusCodes2["MULTIPLE_CHOICES"] = 300] = "MULTIPLE_CHOICES";
|
||||
StatusCodes2[StatusCodes2["MOVED_PERMANENTLY"] = 301] = "MOVED_PERMANENTLY";
|
||||
StatusCodes2[StatusCodes2["MOVED_TEMPORARILY"] = 302] = "MOVED_TEMPORARILY";
|
||||
StatusCodes2[StatusCodes2["SEE_OTHER"] = 303] = "SEE_OTHER";
|
||||
StatusCodes2[StatusCodes2["NOT_MODIFIED"] = 304] = "NOT_MODIFIED";
|
||||
StatusCodes2[StatusCodes2["USE_PROXY"] = 305] = "USE_PROXY";
|
||||
StatusCodes2[StatusCodes2["TEMPORARY_REDIRECT"] = 307] = "TEMPORARY_REDIRECT";
|
||||
StatusCodes2[StatusCodes2["PERMANENT_REDIRECT"] = 308] = "PERMANENT_REDIRECT";
|
||||
StatusCodes2[StatusCodes2["BAD_REQUEST"] = 400] = "BAD_REQUEST";
|
||||
StatusCodes2[StatusCodes2["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
|
||||
StatusCodes2[StatusCodes2["PAYMENT_REQUIRED"] = 402] = "PAYMENT_REQUIRED";
|
||||
StatusCodes2[StatusCodes2["FORBIDDEN"] = 403] = "FORBIDDEN";
|
||||
StatusCodes2[StatusCodes2["NOT_FOUND"] = 404] = "NOT_FOUND";
|
||||
StatusCodes2[StatusCodes2["METHOD_NOT_ALLOWED"] = 405] = "METHOD_NOT_ALLOWED";
|
||||
StatusCodes2[StatusCodes2["NOT_ACCEPTABLE"] = 406] = "NOT_ACCEPTABLE";
|
||||
StatusCodes2[StatusCodes2["PROXY_AUTHENTICATION_REQUIRED"] = 407] = "PROXY_AUTHENTICATION_REQUIRED";
|
||||
StatusCodes2[StatusCodes2["REQUEST_TIMEOUT"] = 408] = "REQUEST_TIMEOUT";
|
||||
StatusCodes2[StatusCodes2["CONFLICT"] = 409] = "CONFLICT";
|
||||
StatusCodes2[StatusCodes2["GONE"] = 410] = "GONE";
|
||||
StatusCodes2[StatusCodes2["LENGTH_REQUIRED"] = 411] = "LENGTH_REQUIRED";
|
||||
StatusCodes2[StatusCodes2["PRECONDITION_FAILED"] = 412] = "PRECONDITION_FAILED";
|
||||
StatusCodes2[StatusCodes2["REQUEST_TOO_LONG"] = 413] = "REQUEST_TOO_LONG";
|
||||
StatusCodes2[StatusCodes2["REQUEST_URI_TOO_LONG"] = 414] = "REQUEST_URI_TOO_LONG";
|
||||
StatusCodes2[StatusCodes2["UNSUPPORTED_MEDIA_TYPE"] = 415] = "UNSUPPORTED_MEDIA_TYPE";
|
||||
StatusCodes2[StatusCodes2["REQUESTED_RANGE_NOT_SATISFIABLE"] = 416] = "REQUESTED_RANGE_NOT_SATISFIABLE";
|
||||
StatusCodes2[StatusCodes2["EXPECTATION_FAILED"] = 417] = "EXPECTATION_FAILED";
|
||||
StatusCodes2[StatusCodes2["IM_A_TEAPOT"] = 418] = "IM_A_TEAPOT";
|
||||
StatusCodes2[StatusCodes2["INSUFFICIENT_SPACE_ON_RESOURCE"] = 419] = "INSUFFICIENT_SPACE_ON_RESOURCE";
|
||||
StatusCodes2[StatusCodes2["METHOD_FAILURE"] = 420] = "METHOD_FAILURE";
|
||||
StatusCodes2[StatusCodes2["MISDIRECTED_REQUEST"] = 421] = "MISDIRECTED_REQUEST";
|
||||
StatusCodes2[StatusCodes2["UNPROCESSABLE_ENTITY"] = 422] = "UNPROCESSABLE_ENTITY";
|
||||
StatusCodes2[StatusCodes2["LOCKED"] = 423] = "LOCKED";
|
||||
StatusCodes2[StatusCodes2["FAILED_DEPENDENCY"] = 424] = "FAILED_DEPENDENCY";
|
||||
StatusCodes2[StatusCodes2["UPGRADE_REQUIRED"] = 426] = "UPGRADE_REQUIRED";
|
||||
StatusCodes2[StatusCodes2["PRECONDITION_REQUIRED"] = 428] = "PRECONDITION_REQUIRED";
|
||||
StatusCodes2[StatusCodes2["TOO_MANY_REQUESTS"] = 429] = "TOO_MANY_REQUESTS";
|
||||
StatusCodes2[StatusCodes2["REQUEST_HEADER_FIELDS_TOO_LARGE"] = 431] = "REQUEST_HEADER_FIELDS_TOO_LARGE";
|
||||
StatusCodes2[StatusCodes2["UNAVAILABLE_FOR_LEGAL_REASONS"] = 451] = "UNAVAILABLE_FOR_LEGAL_REASONS";
|
||||
StatusCodes2[StatusCodes2["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR";
|
||||
StatusCodes2[StatusCodes2["NOT_IMPLEMENTED"] = 501] = "NOT_IMPLEMENTED";
|
||||
StatusCodes2[StatusCodes2["BAD_GATEWAY"] = 502] = "BAD_GATEWAY";
|
||||
StatusCodes2[StatusCodes2["SERVICE_UNAVAILABLE"] = 503] = "SERVICE_UNAVAILABLE";
|
||||
StatusCodes2[StatusCodes2["GATEWAY_TIMEOUT"] = 504] = "GATEWAY_TIMEOUT";
|
||||
StatusCodes2[StatusCodes2["HTTP_VERSION_NOT_SUPPORTED"] = 505] = "HTTP_VERSION_NOT_SUPPORTED";
|
||||
StatusCodes2[StatusCodes2["INSUFFICIENT_STORAGE"] = 507] = "INSUFFICIENT_STORAGE";
|
||||
StatusCodes2[StatusCodes2["NETWORK_AUTHENTICATION_REQUIRED"] = 511] = "NETWORK_AUTHENTICATION_REQUIRED";
|
||||
return StatusCodes2;
|
||||
})(StatusCodes || {});
|
||||
class HttpError extends Error {
|
||||
constructor(statusCode, message, headers) {
|
||||
super(message);
|
||||
__publicField(this, "statusCode");
|
||||
__publicField(this, "headers");
|
||||
this.statusCode = statusCode;
|
||||
this.headers = headers;
|
||||
}
|
||||
toString() {
|
||||
return `HttpError(${this.statusCode}, ${this.message})`;
|
||||
}
|
||||
toResponse() {
|
||||
const m = this.message;
|
||||
return {
|
||||
headers: this.headers,
|
||||
status: this.statusCode,
|
||||
body: m !== "" ? encodeFallback(m) : void 0
|
||||
};
|
||||
}
|
||||
}
|
||||
function stringHandler(f) {
|
||||
return async (req) => {
|
||||
try {
|
||||
const body = req.body;
|
||||
const resp = await f({
|
||||
uri: req.uri,
|
||||
params: req.params,
|
||||
headers: req.headers,
|
||||
user: req.user,
|
||||
body: body && decodeFallback(body)
|
||||
});
|
||||
if (resp === void 0) {
|
||||
return void 0;
|
||||
}
|
||||
if (typeof resp === "string") {
|
||||
return {
|
||||
status: 200,
|
||||
body: encodeFallback(resp)
|
||||
};
|
||||
}
|
||||
const respBody = resp.body;
|
||||
return {
|
||||
headers: resp.headers,
|
||||
status: resp.status,
|
||||
body: respBody ? encodeFallback(respBody) : void 0
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof HttpError) {
|
||||
return err.toResponse();
|
||||
}
|
||||
return {
|
||||
status: 500,
|
||||
body: encodeFallback(`Uncaught error: ${err}`)
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
function htmlHandler(f) {
|
||||
return async (req) => {
|
||||
try {
|
||||
const body = req.body;
|
||||
const resp = await f({
|
||||
uri: req.uri,
|
||||
params: req.params,
|
||||
headers: req.headers,
|
||||
user: req.user,
|
||||
body: body && decodeFallback(body)
|
||||
});
|
||||
if (resp === void 0) {
|
||||
return void 0;
|
||||
}
|
||||
if (typeof resp === "string") {
|
||||
return {
|
||||
headers: [["content-type", "text/html"]],
|
||||
status: 200,
|
||||
body: encodeFallback(resp)
|
||||
};
|
||||
}
|
||||
const respBody = resp.body;
|
||||
return {
|
||||
headers: [["content-type", "text/html"], ...resp.headers ?? []],
|
||||
status: resp.status,
|
||||
body: respBody ? encodeFallback(respBody) : void 0
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof HttpError) {
|
||||
return err.toResponse();
|
||||
}
|
||||
return {
|
||||
status: 500,
|
||||
body: encodeFallback(`Uncaught error: ${err}`)
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
function jsonHandler(f) {
|
||||
return async (req) => {
|
||||
try {
|
||||
const body = req.body;
|
||||
const resp = await f({
|
||||
uri: req.uri,
|
||||
params: req.params,
|
||||
headers: req.headers,
|
||||
user: req.user,
|
||||
body: body && decodeFallback(body)
|
||||
});
|
||||
if (resp === void 0) {
|
||||
return void 0;
|
||||
}
|
||||
if ("body" in resp) {
|
||||
const r = resp;
|
||||
const rBody = r.body;
|
||||
return {
|
||||
headers: [["content-type", "application/json"], ...r.headers ?? []],
|
||||
status: r.status,
|
||||
body: rBody ? encodeFallback(JSON.stringify(rBody)) : void 0
|
||||
};
|
||||
}
|
||||
return {
|
||||
headers: [["content-type", "application/json"]],
|
||||
status: 200,
|
||||
body: encodeFallback(JSON.stringify(resp))
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof HttpError) {
|
||||
return err.toResponse();
|
||||
}
|
||||
return {
|
||||
headers: [["content-type", "application/json"]],
|
||||
status: 500,
|
||||
body: encodeFallback(`Uncaught error: ${err}`)
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
const routerCallbacks = /* @__PURE__ */ new Map();
|
||||
function isolateId() {
|
||||
return rustyscript.functions.isolate_id();
|
||||
}
|
||||
function addRoute(method, route, callback) {
|
||||
if (isolateId() === 0) {
|
||||
rustyscript.functions.install_route(method, route);
|
||||
console.debug("JS: Added route:", method, route);
|
||||
}
|
||||
routerCallbacks.set(`${method}:${route}`, callback);
|
||||
}
|
||||
async function dispatch(method, route, uri, pathParams, headers, user, body) {
|
||||
const key = `${method}:${route}`;
|
||||
const cb = routerCallbacks.get(key);
|
||||
if (!cb) {
|
||||
throw Error(`Missing callback: ${key}`);
|
||||
}
|
||||
return await cb({
|
||||
uri,
|
||||
params: Object.fromEntries(pathParams),
|
||||
headers: Object.fromEntries(headers),
|
||||
user,
|
||||
body
|
||||
}) ?? {
|
||||
status: 200
|
||||
/* OK */
|
||||
};
|
||||
}
|
||||
globalThis.__dispatch = dispatch;
|
||||
const cronCallbacks = /* @__PURE__ */ new Map();
|
||||
function addCronCallback(name, schedule, cb) {
|
||||
const cronRegex = /^(@(yearly|monthly|weekly|daily|hourly|))|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*)\s*){6,7})$/;
|
||||
const matches = cronRegex.test(schedule);
|
||||
if (!matches) {
|
||||
throw Error(`Not a valid 6/7-component cron schedule: ${schedule}`);
|
||||
}
|
||||
if (isolateId() === 0) {
|
||||
const id = rustyscript.functions.install_job(name, schedule);
|
||||
console.debug(`JS: Added cron job (id=${id}): "${name}"`);
|
||||
cronCallbacks.set(id, cb);
|
||||
}
|
||||
}
|
||||
async function dispatchCron(id) {
|
||||
const cb = cronCallbacks.get(id);
|
||||
if (!cb) {
|
||||
throw Error(`Missing cron callback: ${id}`);
|
||||
}
|
||||
try {
|
||||
await cb();
|
||||
} catch (err) {
|
||||
return `${err}`;
|
||||
}
|
||||
}
|
||||
globalThis.__dispatchCron = dispatchCron;
|
||||
function addPeriodicCallback(milliseconds, cb) {
|
||||
if (isolateId() !== 0) {
|
||||
return () => {
|
||||
};
|
||||
}
|
||||
const handle = setInterval(() => {
|
||||
cb(() => clearInterval(handle));
|
||||
}, milliseconds);
|
||||
return () => clearInterval(handle);
|
||||
}
|
||||
async function query(sql, params) {
|
||||
return await rustyscript.async_functions.query(sql, params);
|
||||
}
|
||||
async function execute(sql, params) {
|
||||
return await rustyscript.async_functions.execute(sql, params);
|
||||
}
|
||||
class Transaction {
|
||||
constructor() {
|
||||
__publicField(this, "finalized");
|
||||
this.finalized = false;
|
||||
}
|
||||
query(queryStr, params) {
|
||||
return rustyscript.functions.transaction_query(queryStr, params);
|
||||
}
|
||||
execute(queryStr, params) {
|
||||
return rustyscript.functions.transaction_execute(queryStr, params);
|
||||
}
|
||||
commit() {
|
||||
this.finalized = true;
|
||||
rustyscript.functions.transaction_commit();
|
||||
}
|
||||
rollback() {
|
||||
this.finalized = true;
|
||||
rustyscript.functions.transaction_rollback();
|
||||
}
|
||||
}
|
||||
async function transaction(f) {
|
||||
await rustyscript.async_functions.transaction_begin();
|
||||
const tx = new Transaction();
|
||||
try {
|
||||
const r = f(tx);
|
||||
if (!tx.finalized) {
|
||||
rustyscript.functions.transaction_rollback();
|
||||
}
|
||||
return r;
|
||||
} catch (e) {
|
||||
rustyscript.functions.transaction_rollback();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
function parsePath(path) {
|
||||
const queryIndex = path.indexOf("?");
|
||||
if (queryIndex >= 0) {
|
||||
return {
|
||||
path: path.slice(0, queryIndex),
|
||||
query: new URLSearchParams(path.slice(queryIndex + 1))
|
||||
};
|
||||
}
|
||||
return {
|
||||
path,
|
||||
query: new URLSearchParams()
|
||||
};
|
||||
}
|
||||
const _logStderr = function(...args) {
|
||||
globalThis.Deno.core.print(
|
||||
`${args.join(" ")}
|
||||
`,
|
||||
/* to stderr = */
|
||||
true
|
||||
);
|
||||
};
|
||||
globalThis.console.log = _logStderr;
|
||||
globalThis.console.info = _logStderr;
|
||||
globalThis.console.debug = _logStderr;
|
||||
export {
|
||||
HttpError,
|
||||
StatusCodes,
|
||||
Transaction,
|
||||
addCronCallback,
|
||||
addPeriodicCallback,
|
||||
addRoute,
|
||||
execute,
|
||||
fs,
|
||||
htmlHandler,
|
||||
jsonHandler,
|
||||
parsePath,
|
||||
query,
|
||||
stringHandler,
|
||||
transaction
|
||||
};
|
||||
@@ -81,13 +81,20 @@ const columns: ColumnDef<LogJson>[] = [
|
||||
{ accessorKey: "method" },
|
||||
{ accessorKey: "url" },
|
||||
{
|
||||
accessorKey: "latency_ms",
|
||||
header: "Latency (ms)",
|
||||
accessorKey: "latency_ms",
|
||||
},
|
||||
{ accessorKey: "client_ip" },
|
||||
{
|
||||
accessorKey: "client_cc",
|
||||
header: "Country Code",
|
||||
header: "GeoIP",
|
||||
cell: (ctx) => {
|
||||
const row = ctx.row.original;
|
||||
const city = row.client_geoip_city;
|
||||
if (city) {
|
||||
return `${city.name} (${city.country_code})`;
|
||||
}
|
||||
return row.client_geoip_cc;
|
||||
},
|
||||
},
|
||||
{ accessorKey: "referer" },
|
||||
{
|
||||
|
||||
3
trailbase-assets/js/bindings/GeoipCity.ts
Normal file
3
trailbase-assets/js/bindings/GeoipCity.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type GeoipCity = { country_code: string | null, name: string | null, };
|
||||
@@ -1,7 +1,8 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { GeoipCity } from "./GeoipCity";
|
||||
|
||||
export type LogJson = { id: bigint, created: number, status: number, method: string, url: string, latency_ms: number, client_ip: string,
|
||||
/**
|
||||
* Optional two-letter country code.
|
||||
*/
|
||||
client_cc: string | null, referer: string, user_agent: string, user_id: string | null, };
|
||||
client_geoip_cc: string | null, client_geoip_city: GeoipCity | null, referer: string, user_agent: string, user_id: string | null, };
|
||||
|
||||
@@ -8,6 +8,7 @@ use log::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use trailbase_extension::geoip::{City, DatabaseType};
|
||||
use trailbase_qs::{Cursor, Order, OrderPrecedent, Query};
|
||||
use ts_rs::TS;
|
||||
use uuid::Uuid;
|
||||
@@ -18,6 +19,21 @@ use crate::constants::{LOGS_RETENTION_DEFAULT, LOGS_TABLE_ID_COLUMN};
|
||||
use crate::listing::{WhereClause, build_filter_where_clause, cursor_to_value, limit_or_default};
|
||||
use crate::schema_metadata::{TableMetadata, lookup_and_parse_table_schema};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, TS)]
|
||||
pub struct GeoipCity {
|
||||
country_code: Option<String>,
|
||||
name: Option<String>,
|
||||
}
|
||||
|
||||
impl From<City> for GeoipCity {
|
||||
fn from(city: City) -> Self {
|
||||
return Self {
|
||||
country_code: city.country_code,
|
||||
name: city.name,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, TS)]
|
||||
pub struct LogJson {
|
||||
pub id: i64,
|
||||
@@ -30,7 +46,8 @@ pub struct LogJson {
|
||||
pub latency_ms: f64,
|
||||
pub client_ip: String,
|
||||
/// Optional two-letter country code.
|
||||
pub client_cc: Option<String>,
|
||||
pub client_geoip_cc: Option<String>,
|
||||
pub client_geoip_city: Option<GeoipCity>,
|
||||
|
||||
pub referer: String,
|
||||
pub user_agent: String,
|
||||
@@ -50,7 +67,9 @@ struct LogEntry {
|
||||
latency: f64,
|
||||
client_ip: String,
|
||||
/// Optional two-letter country code.
|
||||
client_cc: Option<String>,
|
||||
client_geoip_cc: Option<String>,
|
||||
/// Optional city JSON.
|
||||
client_geoip_city: Option<String>,
|
||||
|
||||
referer: String,
|
||||
user_agent: String,
|
||||
@@ -82,7 +101,16 @@ impl From<LogEntry> for LogJson {
|
||||
url: value.url,
|
||||
latency_ms: value.latency,
|
||||
client_ip: value.client_ip,
|
||||
client_cc: value.client_cc,
|
||||
client_geoip_cc: value.client_geoip_cc,
|
||||
client_geoip_city: value.client_geoip_city.and_then(|city| {
|
||||
return serde_json::from_str::<City>(&city)
|
||||
.map_err(|err| {
|
||||
log::warn!("Failed to parse geoip city json: {err}");
|
||||
return err;
|
||||
})
|
||||
.map(|city| city.into())
|
||||
.ok();
|
||||
}),
|
||||
referer: value.referer,
|
||||
user_agent: value.user_agent,
|
||||
user_id: value.user_id.map(|blob| Uuid::from_bytes(blob).to_string()),
|
||||
@@ -147,8 +175,10 @@ pub async fn list_logs_handler(
|
||||
}
|
||||
|
||||
let first_page = cursor.is_none();
|
||||
let geoip_db_type = trailbase_extension::geoip::database_type();
|
||||
let mut logs = fetch_logs(
|
||||
conn,
|
||||
geoip_db_type.clone(),
|
||||
filter_where_clause.clone(),
|
||||
cursor,
|
||||
order.as_ref().unwrap_or_else(|| &DEFAULT_ORDERING),
|
||||
@@ -162,9 +192,10 @@ pub async fn list_logs_handler(
|
||||
}
|
||||
}
|
||||
|
||||
let stats = {
|
||||
let stats = if first_page {
|
||||
let now = Utc::now();
|
||||
let args = FetchAggregateArgs {
|
||||
geoip_db_type,
|
||||
filter_where_clause: Some(filter_where_clause),
|
||||
from: now
|
||||
- Duration::seconds(state.access_config(|c| {
|
||||
@@ -176,17 +207,14 @@ pub async fn list_logs_handler(
|
||||
interval: Duration::seconds(600),
|
||||
};
|
||||
|
||||
match first_page {
|
||||
true => {
|
||||
let stats = fetch_aggregate_stats(conn, &args).await;
|
||||
|
||||
if let Err(ref err) = stats {
|
||||
warn!("Failed to fetch stats for {args:?}: {err}");
|
||||
}
|
||||
stats.ok()
|
||||
}
|
||||
false => None,
|
||||
let stats = fetch_aggregate_stats(conn, &args).await;
|
||||
if let Err(ref err) = stats {
|
||||
warn!("Failed to fetch stats for {args:?}: {err}");
|
||||
}
|
||||
|
||||
stats.ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let response = ListLogsResponse {
|
||||
@@ -204,6 +232,7 @@ pub async fn list_logs_handler(
|
||||
|
||||
async fn fetch_logs(
|
||||
conn: &trailbase_sqlite::Connection,
|
||||
geoip_db_type: Option<DatabaseType>,
|
||||
filter_where_clause: WhereClause,
|
||||
cursor: Option<Cursor>,
|
||||
order: &Order,
|
||||
@@ -238,7 +267,7 @@ async fn fetch_logs(
|
||||
|
||||
let sql_query = format!(
|
||||
r#"
|
||||
SELECT log.*, geoip_country(log.client_ip) AS client_cc
|
||||
SELECT log.*, {geoip}
|
||||
FROM
|
||||
(SELECT * FROM {LOGS_TABLE_NAME}) AS log
|
||||
WHERE
|
||||
@@ -247,6 +276,11 @@ async fn fetch_logs(
|
||||
{order_clause}
|
||||
LIMIT :limit
|
||||
"#,
|
||||
geoip = match geoip_db_type {
|
||||
Some(DatabaseType::GeoLite2Country) => "geoip_country(log.client_ip) AS client_geoip_cc",
|
||||
Some(DatabaseType::GeoLite2City) => "geoip_city_json(log.client_ip) AS client_geoip_city",
|
||||
_ => "''",
|
||||
},
|
||||
);
|
||||
|
||||
return Ok(
|
||||
@@ -266,6 +300,7 @@ pub struct Stats {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FetchAggregateArgs {
|
||||
geoip_db_type: Option<DatabaseType>,
|
||||
filter_where_clause: Option<WhereClause>,
|
||||
from: DateTime<Utc>,
|
||||
to: DateTime<Utc>,
|
||||
@@ -345,7 +380,9 @@ async fn fetch_aggregate_stats(
|
||||
));
|
||||
}
|
||||
|
||||
if trailbase_extension::geoip::has_geoip_db() {
|
||||
if args.geoip_db_type == Some(DatabaseType::GeoLite2Country)
|
||||
|| args.geoip_db_type == Some(DatabaseType::GeoLite2City)
|
||||
{
|
||||
lazy_static! {
|
||||
static ref CC_QUERY: String = format!(
|
||||
r#"
|
||||
@@ -439,6 +476,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let args = FetchAggregateArgs {
|
||||
geoip_db_type: Some(DatabaseType::Unknown),
|
||||
filter_where_clause: None,
|
||||
from: from.into(),
|
||||
to: to.into(),
|
||||
|
||||
@@ -14,9 +14,9 @@ static READER: LazyLock<ArcSwap<Option<MaxMindReader>>> =
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
||||
pub struct City {
|
||||
country_code: Option<String>,
|
||||
name: Option<String>,
|
||||
subdivisions: Option<Vec<String>>,
|
||||
pub country_code: Option<String>,
|
||||
pub name: Option<String>,
|
||||
pub subdivisions: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl City {
|
||||
@@ -34,6 +34,7 @@ impl City {
|
||||
|
||||
pub fn load_geoip_db(path: impl AsRef<Path>) -> Result<(), MaxMindDbError> {
|
||||
let reader = Reader::open_readfile(path)?;
|
||||
log::debug!("Loaded geoip DB: {:?}", reader.metadata);
|
||||
READER.swap(Some(reader).into());
|
||||
return Ok(());
|
||||
}
|
||||
@@ -42,6 +43,27 @@ pub fn has_geoip_db() -> bool {
|
||||
return READER.load().is_some();
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum DatabaseType {
|
||||
Unknown,
|
||||
GeoLite2Country,
|
||||
GeoLite2City,
|
||||
GeoLite2ASN,
|
||||
}
|
||||
|
||||
pub fn database_type() -> Option<DatabaseType> {
|
||||
if let Some(ref reader) = **READER.load() {
|
||||
return Some(match reader.metadata.database_type.as_str() {
|
||||
"GeoLite2-Country" => DatabaseType::GeoLite2Country,
|
||||
"GeoLite2-City" => DatabaseType::GeoLite2City,
|
||||
// Autonomous system number.
|
||||
"GeoLite2-ASN" => DatabaseType::GeoLite2ASN,
|
||||
_ => DatabaseType::Unknown,
|
||||
});
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
pub(crate) fn geoip_country(context: &Context) -> Result<Option<String>, Error> {
|
||||
return geoip_extract(context, |reader, client_ip| {
|
||||
if let Ok(Some(country)) = reader.lookup::<geoip2::Country>(client_ip) {
|
||||
|
||||
Reference in New Issue
Block a user