basic database
This commit is contained in:
		
							parent
							
								
									1a3c6f2209
								
							
						
					
					
						commit
						8be8d0ebe1
					
				| 
						 | 
				
			
			@ -0,0 +1,45 @@
 | 
			
		|||
{
 | 
			
		||||
	// Use IntelliSense to learn about possible attributes.
 | 
			
		||||
	// Hover to view descriptions of existing attributes.
 | 
			
		||||
	// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
 | 
			
		||||
	"version": "0.2.0",
 | 
			
		||||
	"configurations": [
 | 
			
		||||
		{
 | 
			
		||||
			"type": "lldb",
 | 
			
		||||
			"request": "launch",
 | 
			
		||||
			"name": "Debug executable 'flabk'",
 | 
			
		||||
			"cargo": {
 | 
			
		||||
				"args": [
 | 
			
		||||
					"build",
 | 
			
		||||
					"--bin=flabk",
 | 
			
		||||
					"--package=flabk"
 | 
			
		||||
				],
 | 
			
		||||
				"filter": {
 | 
			
		||||
					"name": "flabk",
 | 
			
		||||
					"kind": "bin"
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			"args": [],
 | 
			
		||||
			"cwd": "${workspaceFolder}"
 | 
			
		||||
		},
 | 
			
		||||
		// {
 | 
			
		||||
		// 	"type": "lldb",
 | 
			
		||||
		// 	"request": "launch",
 | 
			
		||||
		// 	"name": "Debug unit tests in executable 'flabk'",
 | 
			
		||||
		// 	"cargo": {
 | 
			
		||||
		// 		"args": [
 | 
			
		||||
		// 			"test",
 | 
			
		||||
		// 			"--no-run",
 | 
			
		||||
		// 			"--bin=flabk",
 | 
			
		||||
		// 			"--package=flabk"
 | 
			
		||||
		// 		],
 | 
			
		||||
		// 		"filter": {
 | 
			
		||||
		// 			"name": "flabk",
 | 
			
		||||
		// 			"kind": "bin"
 | 
			
		||||
		// 		}
 | 
			
		||||
		// 	},
 | 
			
		||||
		// 	"args": [],
 | 
			
		||||
		// 	"cwd": "${workspaceFolder}"
 | 
			
		||||
		// }
 | 
			
		||||
	]
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2,18 +2,71 @@
 | 
			
		|||
# It is not intended for manual editing.
 | 
			
		||||
version = 3
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "addr2line"
 | 
			
		||||
version = "0.17.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "gimli",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "adler"
 | 
			
		||||
version = "1.0.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "anyhow"
 | 
			
		||||
version = "1.0.64"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b9a8f622bcf6ff3df478e9deba3e03e4e04b300f8e6a139e192c05fa3490afc7"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "async-trait"
 | 
			
		||||
version = "0.1.57"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "syn",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "autocfg"
 | 
			
		||||
version = "1.1.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "backtrace"
 | 
			
		||||
version = "0.3.66"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "addr2line",
 | 
			
		||||
 "cc",
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
 "libc",
 | 
			
		||||
 "miniz_oxide",
 | 
			
		||||
 "object",
 | 
			
		||||
 "rustc-demangle",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "base-62"
 | 
			
		||||
version = "0.1.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f28ebd71b3e708e895b83ec2d35c6e2ef96e34945706bf4d73826354e84f89b2"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "failure",
 | 
			
		||||
 "num-bigint",
 | 
			
		||||
 "num-integer",
 | 
			
		||||
 "num-traits",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "base64"
 | 
			
		||||
version = "0.13.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -66,6 +119,12 @@ version = "1.2.1"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "cc"
 | 
			
		||||
version = "1.0.73"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "cfg-if"
 | 
			
		||||
version = "1.0.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -108,8 +167,37 @@ checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
 | 
			
		|||
dependencies = [
 | 
			
		||||
 "block-buffer 0.10.3",
 | 
			
		||||
 "crypto-common",
 | 
			
		||||
 "subtle",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "failure"
 | 
			
		||||
version = "0.1.8"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "backtrace",
 | 
			
		||||
 "failure_derive",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "failure_derive"
 | 
			
		||||
version = "0.1.8"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "syn",
 | 
			
		||||
 "synstructure",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "fallible-iterator"
 | 
			
		||||
version = "0.2.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "fastrand"
 | 
			
		||||
version = "1.8.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -124,10 +212,13 @@ name = "flabk"
 | 
			
		|||
version = "0.0.1"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "anyhow",
 | 
			
		||||
 "base-62",
 | 
			
		||||
 "handlebars",
 | 
			
		||||
 "rand",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
 "tokio",
 | 
			
		||||
 "tokio-postgres",
 | 
			
		||||
 "warp",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -162,6 +253,17 @@ version = "0.3.24"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "futures-macro"
 | 
			
		||||
version = "0.3.24"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "syn",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "futures-sink"
 | 
			
		||||
version = "0.3.24"
 | 
			
		||||
| 
						 | 
				
			
			@ -181,6 +283,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		|||
checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "futures-core",
 | 
			
		||||
 "futures-macro",
 | 
			
		||||
 "futures-sink",
 | 
			
		||||
 "futures-task",
 | 
			
		||||
 "pin-project-lite",
 | 
			
		||||
| 
						 | 
				
			
			@ -209,6 +312,12 @@ dependencies = [
 | 
			
		|||
 "wasi",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "gimli"
 | 
			
		||||
version = "0.26.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "h2"
 | 
			
		||||
version = "0.3.14"
 | 
			
		||||
| 
						 | 
				
			
			@ -282,6 +391,15 @@ dependencies = [
 | 
			
		|||
 "libc",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "hmac"
 | 
			
		||||
version = "0.12.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "digest 0.10.3",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "http"
 | 
			
		||||
version = "0.2.8"
 | 
			
		||||
| 
						 | 
				
			
			@ -400,6 +518,15 @@ dependencies = [
 | 
			
		|||
 "cfg-if",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "md-5"
 | 
			
		||||
version = "0.10.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "66b48670c893079d3c2ed79114e3644b7004df1c361a4e0ad52e2e6940d07c3d"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "digest 0.10.3",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "memchr"
 | 
			
		||||
version = "2.5.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -422,6 +549,15 @@ dependencies = [
 | 
			
		|||
 "unicase",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "miniz_oxide"
 | 
			
		||||
version = "0.5.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "adler",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "mio"
 | 
			
		||||
version = "0.8.4"
 | 
			
		||||
| 
						 | 
				
			
			@ -452,6 +588,36 @@ dependencies = [
 | 
			
		|||
 "twoway",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "num-bigint"
 | 
			
		||||
version = "0.2.6"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "autocfg",
 | 
			
		||||
 "num-integer",
 | 
			
		||||
 "num-traits",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "num-integer"
 | 
			
		||||
version = "0.1.45"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "autocfg",
 | 
			
		||||
 "num-traits",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "num-traits"
 | 
			
		||||
version = "0.2.15"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "autocfg",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "num_cpus"
 | 
			
		||||
version = "1.13.1"
 | 
			
		||||
| 
						 | 
				
			
			@ -462,6 +628,15 @@ dependencies = [
 | 
			
		|||
 "libc",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "object"
 | 
			
		||||
version = "0.29.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "memchr",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "once_cell"
 | 
			
		||||
version = "1.14.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -547,6 +722,24 @@ dependencies = [
 | 
			
		|||
 "sha-1 0.10.0",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "phf"
 | 
			
		||||
version = "0.11.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "phf_shared",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "phf_shared"
 | 
			
		||||
version = "0.11.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "siphasher",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "pin-project"
 | 
			
		||||
version = "1.0.12"
 | 
			
		||||
| 
						 | 
				
			
			@ -579,6 +772,37 @@ version = "0.1.0"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "postgres-protocol"
 | 
			
		||||
version = "0.6.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "878c6cbf956e03af9aa8204b407b9cbf47c072164800aa918c516cd4b056c50c"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "base64",
 | 
			
		||||
 "byteorder",
 | 
			
		||||
 "bytes",
 | 
			
		||||
 "fallible-iterator",
 | 
			
		||||
 "hmac",
 | 
			
		||||
 "md-5",
 | 
			
		||||
 "memchr",
 | 
			
		||||
 "rand",
 | 
			
		||||
 "sha2",
 | 
			
		||||
 "stringprep",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "postgres-types"
 | 
			
		||||
version = "0.2.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "73d946ec7d256b04dfadc4e6a3292324e6f417124750fc5c0950f981b703a0f1"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "bytes",
 | 
			
		||||
 "fallible-iterator",
 | 
			
		||||
 "postgres-protocol",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "ppv-lite86"
 | 
			
		||||
version = "0.2.16"
 | 
			
		||||
| 
						 | 
				
			
			@ -657,6 +881,12 @@ dependencies = [
 | 
			
		|||
 "winapi",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rustc-demangle"
 | 
			
		||||
version = "0.1.21"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "ryu"
 | 
			
		||||
version = "1.0.11"
 | 
			
		||||
| 
						 | 
				
			
			@ -759,6 +989,17 @@ dependencies = [
 | 
			
		|||
 "digest 0.10.3",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "sha2"
 | 
			
		||||
version = "0.10.5"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "cf9db03534dff993187064c4e0c05a5708d2a9728ace9a8959b77bedf415dac5"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
 "cpufeatures",
 | 
			
		||||
 "digest 0.10.3",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "signal-hook-registry"
 | 
			
		||||
version = "1.4.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -768,6 +1009,12 @@ dependencies = [
 | 
			
		|||
 "libc",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "siphasher"
 | 
			
		||||
version = "0.3.10"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "slab"
 | 
			
		||||
version = "0.4.7"
 | 
			
		||||
| 
						 | 
				
			
			@ -793,6 +1040,22 @@ dependencies = [
 | 
			
		|||
 "winapi",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "stringprep"
 | 
			
		||||
version = "0.1.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "unicode-bidi",
 | 
			
		||||
 "unicode-normalization",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "subtle"
 | 
			
		||||
version = "2.4.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "syn"
 | 
			
		||||
version = "1.0.99"
 | 
			
		||||
| 
						 | 
				
			
			@ -804,6 +1067,18 @@ dependencies = [
 | 
			
		|||
 "unicode-ident",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "synstructure"
 | 
			
		||||
version = "0.12.6"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "syn",
 | 
			
		||||
 "unicode-xid",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "tempfile"
 | 
			
		||||
version = "3.3.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -885,6 +1160,30 @@ dependencies = [
 | 
			
		|||
 "syn",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "tokio-postgres"
 | 
			
		||||
version = "0.7.7"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "29a12c1b3e0704ae7dfc25562629798b29c72e6b1d0a681b6f29ab4ae5e7f7bf"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "async-trait",
 | 
			
		||||
 "byteorder",
 | 
			
		||||
 "bytes",
 | 
			
		||||
 "fallible-iterator",
 | 
			
		||||
 "futures-channel",
 | 
			
		||||
 "futures-util",
 | 
			
		||||
 "log",
 | 
			
		||||
 "parking_lot",
 | 
			
		||||
 "percent-encoding",
 | 
			
		||||
 "phf",
 | 
			
		||||
 "pin-project-lite",
 | 
			
		||||
 "postgres-protocol",
 | 
			
		||||
 "postgres-types",
 | 
			
		||||
 "socket2",
 | 
			
		||||
 "tokio",
 | 
			
		||||
 "tokio-util 0.7.4",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "tokio-stream"
 | 
			
		||||
version = "0.1.9"
 | 
			
		||||
| 
						 | 
				
			
			@ -1040,6 +1339,12 @@ dependencies = [
 | 
			
		|||
 "tinyvec",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "unicode-xid"
 | 
			
		||||
version = "0.2.3"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "url"
 | 
			
		||||
version = "2.3.1"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,8 +7,11 @@ edition = "2021"
 | 
			
		|||
 | 
			
		||||
[dependencies]
 | 
			
		||||
anyhow = "1.0.64"
 | 
			
		||||
base-62 = "0.1.1"
 | 
			
		||||
handlebars = "4.3.3"
 | 
			
		||||
rand = "0.8.5"
 | 
			
		||||
serde = { version = "1.0.144", features = ["derive", "std", "serde_derive"]}
 | 
			
		||||
serde_json = "1.0.85"
 | 
			
		||||
tokio = { version = "1", features = ["full"] }
 | 
			
		||||
tokio-postgres = { version = "0.7.7", features = ["with-serde_json-1"] }
 | 
			
		||||
warp = { version = "0.3.2" }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
CREATE TABLE users (
 | 
			
		||||
	id CHAR(22) NOT NULL PRIMARY KEY,
 | 
			
		||||
	username TEXT NOT NULL,
 | 
			
		||||
	host TEXT,
 | 
			
		||||
	display_name TEXT
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
CREATE UNIQUE INDEX u_username_host ON users (username, host);
 | 
			
		||||
CREATE UNIQUE INDEX u_username_local ON users (username) WHERE host IS NULL;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,47 @@
 | 
			
		|||
use std::sync::Arc;
 | 
			
		||||
 | 
			
		||||
use super::users::Users;
 | 
			
		||||
use tokio::sync::Mutex;
 | 
			
		||||
use tokio_postgres::{tls::NoTlsStream, Client, Connection, NoTls, Socket};
 | 
			
		||||
 | 
			
		||||
const DBERR_UNIQUE: &str = "23505";
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub struct DB {
 | 
			
		||||
    client: Arc<Mutex<Client>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DB {
 | 
			
		||||
    pub async fn new(host: String, user: String, database: String) -> Result<Self, anyhow::Error> {
 | 
			
		||||
        let (cl, conn) = tokio_postgres::connect(
 | 
			
		||||
            format!("host={host} user={user} dbname={database}").as_str(),
 | 
			
		||||
            NoTls,
 | 
			
		||||
        )
 | 
			
		||||
        .await?;
 | 
			
		||||
        tokio::spawn(async move {
 | 
			
		||||
            if let Err(e) = conn.await {
 | 
			
		||||
                eprintln!("connection error: {}", e);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        let client = Arc::new(Mutex::new(cl));
 | 
			
		||||
        Ok(Self { client })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn users(&self) -> Users {
 | 
			
		||||
        Users::new(self.client.clone())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub enum DBError {
 | 
			
		||||
    Duplicate,
 | 
			
		||||
    Other(tokio_postgres::Error),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<tokio_postgres::Error> for DBError {
 | 
			
		||||
    fn from(err: tokio_postgres::Error) -> Self {
 | 
			
		||||
        if let Some(code) = err.code() && code.code() == DBERR_UNIQUE {
 | 
			
		||||
			return DBError::Duplicate;
 | 
			
		||||
		}
 | 
			
		||||
        DBError::Other(err)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
pub mod db;
 | 
			
		||||
pub mod users;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,95 @@
 | 
			
		|||
use std::sync::Arc;
 | 
			
		||||
 | 
			
		||||
use rand::Rng;
 | 
			
		||||
use tokio::sync::Mutex;
 | 
			
		||||
use tokio_postgres::{Client, Row};
 | 
			
		||||
 | 
			
		||||
use super::db;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub struct Users(Arc<Mutex<Client>>);
 | 
			
		||||
 | 
			
		||||
impl Users {
 | 
			
		||||
    pub fn new(client: Arc<Mutex<Client>>) -> Self {
 | 
			
		||||
        Self(client)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn new_id() -> String {
 | 
			
		||||
        let bytes = rand::thread_rng().gen::<[u8; 16]>();
 | 
			
		||||
        base_62::encode(&bytes)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn create_user(&self, u: User) -> Result<User, db::DBError> {
 | 
			
		||||
        let row = self.0.lock().await.query_one(
 | 
			
		||||
            "insert into users (id, username, host, display_name) values ($1, $2, $3, $4) returning id",
 | 
			
		||||
            &[&Self::new_id(), &u.username, &u.host, &u.display_name],
 | 
			
		||||
        ).await?;
 | 
			
		||||
        Ok(User {
 | 
			
		||||
            id: row.get("id"),
 | 
			
		||||
            username: u.username,
 | 
			
		||||
            host: u.host,
 | 
			
		||||
            display_name: u.display_name,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn user(&self, by: UserSelect) -> Result<Option<User>, anyhow::Error> {
 | 
			
		||||
        let where_param: String;
 | 
			
		||||
        let where_clause = match by {
 | 
			
		||||
            UserSelect::ID(id) => {
 | 
			
		||||
                where_param = id;
 | 
			
		||||
                "id = $1"
 | 
			
		||||
            }
 | 
			
		||||
            UserSelect::Username(username) => {
 | 
			
		||||
                where_param = username;
 | 
			
		||||
                "username = $1"
 | 
			
		||||
            }
 | 
			
		||||
            UserSelect::FullUsername(full) => {
 | 
			
		||||
                where_param = full;
 | 
			
		||||
                "(username || '@' || host) = $1"
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        let rows = self
 | 
			
		||||
            .0
 | 
			
		||||
            .lock()
 | 
			
		||||
            .await
 | 
			
		||||
            .query(
 | 
			
		||||
                format!(
 | 
			
		||||
                    "select id, username, host, display_name from users where {}",
 | 
			
		||||
                    where_clause
 | 
			
		||||
                )
 | 
			
		||||
                .as_str(),
 | 
			
		||||
                &[&where_param],
 | 
			
		||||
            )
 | 
			
		||||
            .await?;
 | 
			
		||||
 | 
			
		||||
        if let Some(row) = rows.first() && rows.len() == 1 {
 | 
			
		||||
			Ok(Some(User::from(row)))
 | 
			
		||||
		} else {
 | 
			
		||||
			Ok(None)
 | 
			
		||||
		}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct User {
 | 
			
		||||
    pub id: String,
 | 
			
		||||
    pub username: String,
 | 
			
		||||
    pub host: Option<String>,
 | 
			
		||||
    pub display_name: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&Row> for User {
 | 
			
		||||
    fn from(row: &Row) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            id: row.get("id"),
 | 
			
		||||
            username: row.get("username"),
 | 
			
		||||
            host: row.get("host"),
 | 
			
		||||
            display_name: row.get("display_name"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub enum UserSelect {
 | 
			
		||||
    ID(String),
 | 
			
		||||
    Username(String),
 | 
			
		||||
    FullUsername(String),
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
use warp::hyper::StatusCode;
 | 
			
		||||
 | 
			
		||||
pub(crate) fn html_with_status(body: String, status: StatusCode) -> warp::http::Response<String> {
 | 
			
		||||
    warp::http::Response::builder()
 | 
			
		||||
        .header("Content-Type", "text/html; charset=utf-8")
 | 
			
		||||
        .status(status)
 | 
			
		||||
        .body(body)
 | 
			
		||||
        .expect("failed marshalling html response")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn html(body: String) -> warp::http::Response<String> {
 | 
			
		||||
    html_with_status(body, StatusCode::OK)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// pub(crate) fn html(body: String) -> Result<warp::http::Response<String>, warp::http::Error> {
 | 
			
		||||
//     warp::http::Response::builder()
 | 
			
		||||
//         .header("Content-Type", "text/html; charset=utf-8")
 | 
			
		||||
//         .status(StatusCode::OK)
 | 
			
		||||
//         .body(body)
 | 
			
		||||
// }
 | 
			
		||||
							
								
								
									
										18
									
								
								src/main.rs
								
								
								
								
							
							
						
						
									
										18
									
								
								src/main.rs
								
								
								
								
							| 
						 | 
				
			
			@ -1,7 +1,13 @@
 | 
			
		|||
mod database;
 | 
			
		||||
mod helpers;
 | 
			
		||||
mod model;
 | 
			
		||||
mod servek;
 | 
			
		||||
mod svc;
 | 
			
		||||
 | 
			
		||||
use database::db::DB;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use servek::Server;
 | 
			
		||||
use servek::servek::Server;
 | 
			
		||||
use svc::profiles::Profiler;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, Serialize, Deserialize)]
 | 
			
		||||
pub enum ActivityKind {
 | 
			
		||||
| 
						 | 
				
			
			@ -31,7 +37,13 @@ pub struct ObjectLD {
 | 
			
		|||
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
async fn main() -> Result<(), anyhow::Error> {
 | 
			
		||||
    Server::new().listen_and_serve(8008).await;
 | 
			
		||||
 | 
			
		||||
    let db = DB::new(
 | 
			
		||||
        "localhost".to_owned(),
 | 
			
		||||
        "flabk".to_owned(),
 | 
			
		||||
        "flabk".to_owned(),
 | 
			
		||||
    )
 | 
			
		||||
    .await?;
 | 
			
		||||
    let profiler = Profiler::new(db.users());
 | 
			
		||||
    Server::new(profiler).listen_and_serve(8008).await;
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
use serde::Serialize;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Serialize)]
 | 
			
		||||
pub struct Error<'a> {
 | 
			
		||||
    pub error: &'a str,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Error<'a> {
 | 
			
		||||
    pub fn error(error: &'a str) -> Self {
 | 
			
		||||
        Self { error }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										116
									
								
								src/servek.rs
								
								
								
								
							
							
						
						
									
										116
									
								
								src/servek.rs
								
								
								
								
							| 
						 | 
				
			
			@ -1,116 +0,0 @@
 | 
			
		|||
use core::panic;
 | 
			
		||||
use std::{convert::Infallible, fmt::Display};
 | 
			
		||||
 | 
			
		||||
use handlebars::{Handlebars, RenderError};
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
use warp::{hyper::StatusCode, reject::Reject, Filter, Rejection, Reply};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct Server {
 | 
			
		||||
    hb: Handlebars<'static>,
 | 
			
		||||
    profiler: Profiler,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
struct Profiler;
 | 
			
		||||
 | 
			
		||||
impl Profiler {
 | 
			
		||||
    fn profile(&self, username: String) -> Result<Profile, Infallible> {
 | 
			
		||||
        Ok(Profile { username })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Server {
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        let mut hb = Handlebars::new();
 | 
			
		||||
        hb.register_template_string("profile", include_str!("../templates/html/profile.html"))
 | 
			
		||||
            .expect("profile template");
 | 
			
		||||
        let profiler = Profiler;
 | 
			
		||||
 | 
			
		||||
        Self { hb, profiler }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn listen_and_serve(self, port: u16) -> ! {
 | 
			
		||||
        println!("starting server on port {}", port);
 | 
			
		||||
        warp::serve(self.html().recover(Self::handle_rejection))
 | 
			
		||||
            .run(([127, 0, 0, 1], port))
 | 
			
		||||
            .await;
 | 
			
		||||
        panic!("server stopped prematurely")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn html(&self) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
 | 
			
		||||
        Self::index().or(self.profile())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn index() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
 | 
			
		||||
        warp::get().and(warp::path::end().map(move || {
 | 
			
		||||
            warp::reply::html(include_str!("../templates/html/index.html").to_owned())
 | 
			
		||||
        }))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn with_server(
 | 
			
		||||
        srv: Server,
 | 
			
		||||
    ) -> impl Filter<Extract = (Server,), Error = std::convert::Infallible> + Clone {
 | 
			
		||||
        warp::any().map(move || srv.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn profile(&self) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
 | 
			
		||||
        warp::get().and(
 | 
			
		||||
            warp::path!("@" / String)
 | 
			
		||||
                .and(Self::with_server(self.clone()))
 | 
			
		||||
                .and_then(|username: String, srv: Server| async move {
 | 
			
		||||
                    match srv.hb.render(
 | 
			
		||||
                        "profile",
 | 
			
		||||
                        &serde_json::json!(srv.profiler.profile(username)?),
 | 
			
		||||
                    ) {
 | 
			
		||||
                        Ok(html) => Ok(warp::reply::html(html)),
 | 
			
		||||
                        Err(err) => Err(InternalError::reject(err.to_string())),
 | 
			
		||||
                    }
 | 
			
		||||
                }),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn handle_rejection(err: Rejection) -> Result<impl Reply, Infallible> {
 | 
			
		||||
        if let Some(internal) = err.find::<InternalError>() {
 | 
			
		||||
            println!("internal error: {}", internal);
 | 
			
		||||
            return Ok(warp::reply::with_status(
 | 
			
		||||
                "internal server error",
 | 
			
		||||
                StatusCode::INTERNAL_SERVER_ERROR,
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        panic!()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
struct InternalError {
 | 
			
		||||
    inner: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl InternalError {
 | 
			
		||||
    fn reject(err: String) -> Rejection {
 | 
			
		||||
        warp::reject::custom(Self { inner: err })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Reject for InternalError {}
 | 
			
		||||
 | 
			
		||||
impl From<RenderError> for InternalError {
 | 
			
		||||
    fn from(r: RenderError) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            inner: r.to_string(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Display for InternalError {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        write!(f, "{}", self.inner)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize)]
 | 
			
		||||
struct Profile {
 | 
			
		||||
    username: String,
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,68 @@
 | 
			
		|||
use std::str::FromStr;
 | 
			
		||||
 | 
			
		||||
use warp::{hyper::Uri, Filter, Rejection, Reply};
 | 
			
		||||
 | 
			
		||||
use super::servek::{Server, ServerError};
 | 
			
		||||
 | 
			
		||||
impl Server {
 | 
			
		||||
    pub(super) async fn html(
 | 
			
		||||
        &self,
 | 
			
		||||
    ) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
 | 
			
		||||
        Self::index()
 | 
			
		||||
            .or(self.profile().await)
 | 
			
		||||
            .or(self.create_profile().await)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn index() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
 | 
			
		||||
        warp::get().and(warp::path::end().map(move || {
 | 
			
		||||
            warp::reply::html(include_str!("../../templates/html/index.html").to_owned())
 | 
			
		||||
        }))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn with_server(
 | 
			
		||||
        srv: Server,
 | 
			
		||||
    ) -> impl Filter<Extract = (Server,), Error = std::convert::Infallible> + Clone {
 | 
			
		||||
        warp::any().map(move || srv.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn profile(&self) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
 | 
			
		||||
        warp::get().and(
 | 
			
		||||
            warp::path!("@" / String)
 | 
			
		||||
                .and(Self::with_server(self.clone()))
 | 
			
		||||
                .and_then(|username: String, srv: Server| async move {
 | 
			
		||||
                    match srv
 | 
			
		||||
                        .hb
 | 
			
		||||
                        .render(
 | 
			
		||||
                            "profile",
 | 
			
		||||
                            &serde_json::json!(srv.profiler.profile(username).await?),
 | 
			
		||||
                        )
 | 
			
		||||
                        .map(|html| warp::reply::html(html))
 | 
			
		||||
                        .map_err(|e| ServerError::from(e).reject_self())
 | 
			
		||||
                    {
 | 
			
		||||
                        Ok(resp) => Ok(resp),
 | 
			
		||||
                        Err(err) => Err(err),
 | 
			
		||||
                    }
 | 
			
		||||
                }),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn create_profile(&self) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
 | 
			
		||||
        warp::post().and(
 | 
			
		||||
            warp::path!("@" / String)
 | 
			
		||||
                .and(Self::with_server(self.clone()))
 | 
			
		||||
                .and_then(|username: String, srv: Server| async move {
 | 
			
		||||
                    let user = srv
 | 
			
		||||
                        .profiler
 | 
			
		||||
                        .create_user(username, None)
 | 
			
		||||
                        .await
 | 
			
		||||
                        .map_err(|e| ServerError::from(e));
 | 
			
		||||
                    match user {
 | 
			
		||||
                        Ok(u) => Ok(warp::redirect(
 | 
			
		||||
                            Uri::from_str(format!("/@/{}", u.username).as_str()).unwrap(),
 | 
			
		||||
                        )),
 | 
			
		||||
                        Err(e) => Err(e.reject_self()),
 | 
			
		||||
                    }
 | 
			
		||||
                }),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
mod html;
 | 
			
		||||
pub mod servek;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,111 @@
 | 
			
		|||
use core::panic;
 | 
			
		||||
use std::{convert::Infallible, fmt::Display};
 | 
			
		||||
 | 
			
		||||
use handlebars::{Handlebars, RenderError};
 | 
			
		||||
use warp::{hyper::StatusCode, reject::Reject, Filter, Rejection, Reply};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    model,
 | 
			
		||||
    svc::profiles::{Profiler, UserError},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub struct Server {
 | 
			
		||||
    pub(super) hb: Handlebars<'static>,
 | 
			
		||||
    pub(super) profiler: Profiler,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Server {
 | 
			
		||||
    pub fn new(profiler: Profiler) -> Self {
 | 
			
		||||
        let mut hb = Handlebars::new();
 | 
			
		||||
        hb.register_template_string("profile", include_str!("../../templates/html/profile.html"))
 | 
			
		||||
            .expect("profile template");
 | 
			
		||||
 | 
			
		||||
        Self { hb, profiler }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn listen_and_serve(self, port: u16) -> ! {
 | 
			
		||||
        println!("starting server on port {}", port);
 | 
			
		||||
        warp::serve(self.html().await.recover(Self::handle_rejection))
 | 
			
		||||
            .run(([127, 0, 0, 1], port))
 | 
			
		||||
            .await;
 | 
			
		||||
        panic!("server stopped prematurely")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn handle_rejection(err: Rejection) -> Result<impl Reply, Infallible> {
 | 
			
		||||
        let code;
 | 
			
		||||
        let message;
 | 
			
		||||
 | 
			
		||||
        if err.is_not_found() {
 | 
			
		||||
            code = StatusCode::NOT_FOUND;
 | 
			
		||||
            message = "not found";
 | 
			
		||||
        } else if let Some(err) = err.find::<ServerError>() {
 | 
			
		||||
            match err {
 | 
			
		||||
                ServerError::Internal(err) => {
 | 
			
		||||
                    println!("internal server error: {}", err);
 | 
			
		||||
                    code = StatusCode::INTERNAL_SERVER_ERROR;
 | 
			
		||||
                    message = "internal server error";
 | 
			
		||||
                }
 | 
			
		||||
                ServerError::User(u) => match u {
 | 
			
		||||
                    UserError::Duplicate => {
 | 
			
		||||
                        code = StatusCode::BAD_REQUEST;
 | 
			
		||||
                        message = "duplicate entry";
 | 
			
		||||
                    }
 | 
			
		||||
                    UserError::Other(_) => {
 | 
			
		||||
                        panic!("FIXME: other case should already be handled in conversions")
 | 
			
		||||
                    }
 | 
			
		||||
                    UserError::NotFound => {
 | 
			
		||||
                        code = StatusCode::NOT_FOUND;
 | 
			
		||||
                        message = "not found";
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // We should have expected this... Just log and say its a 500
 | 
			
		||||
            println!("FIXME: unhandled rejection: {:?}", err);
 | 
			
		||||
            code = StatusCode::INTERNAL_SERVER_ERROR;
 | 
			
		||||
            message = "internal server error"
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(warp::reply::with_status(
 | 
			
		||||
            warp::reply::json(&model::Error::error(message)),
 | 
			
		||||
            code,
 | 
			
		||||
        ))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub(super) enum ServerError {
 | 
			
		||||
    Internal(String),
 | 
			
		||||
    User(UserError),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ServerError {
 | 
			
		||||
    pub(super) fn reject_self(self) -> Rejection {
 | 
			
		||||
        warp::reject::custom(self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Reject for ServerError {}
 | 
			
		||||
 | 
			
		||||
impl From<RenderError> for ServerError {
 | 
			
		||||
    fn from(r: RenderError) -> Self {
 | 
			
		||||
        Self::Internal(r.to_string())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<UserError> for ServerError {
 | 
			
		||||
    fn from(u: UserError) -> Self {
 | 
			
		||||
        match u {
 | 
			
		||||
            UserError::Duplicate => Self::User(u),
 | 
			
		||||
            UserError::NotFound => Self::User(u),
 | 
			
		||||
            UserError::Other(o) => Self::Internal(format!("UserError: {}", o)),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Display for ServerError {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        write!(f, "{}", self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
pub mod profiles;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,115 @@
 | 
			
		|||
use serde::Serialize;
 | 
			
		||||
use warp::{reject::Reject, Rejection};
 | 
			
		||||
 | 
			
		||||
use crate::database::{
 | 
			
		||||
    db,
 | 
			
		||||
    users::{self, UserSelect, Users},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub struct Profiler {
 | 
			
		||||
    db: Users,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Profiler {
 | 
			
		||||
    pub fn new(db: Users) -> Self {
 | 
			
		||||
        Self { db }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn profile(&self, username: String) -> Result<User, UserError> {
 | 
			
		||||
        let select = if username.contains("@") {
 | 
			
		||||
            UserSelect::FullUsername(username)
 | 
			
		||||
        } else {
 | 
			
		||||
            UserSelect::Username(username)
 | 
			
		||||
        };
 | 
			
		||||
        match self.db.user(select).await? {
 | 
			
		||||
            Some(user) => Ok(User::from(user)),
 | 
			
		||||
            None => Err(UserError::NotFound),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn create_user(
 | 
			
		||||
        &self,
 | 
			
		||||
        username: String,
 | 
			
		||||
        display_name: Option<String>,
 | 
			
		||||
    ) -> Result<User, UserError> {
 | 
			
		||||
        let result = self
 | 
			
		||||
            .db
 | 
			
		||||
            .create_user(
 | 
			
		||||
                User {
 | 
			
		||||
                    id: String::new(),
 | 
			
		||||
                    username,
 | 
			
		||||
                    display_name,
 | 
			
		||||
                    host: None,
 | 
			
		||||
                }
 | 
			
		||||
                .into(),
 | 
			
		||||
            )
 | 
			
		||||
            .await?;
 | 
			
		||||
 | 
			
		||||
        Ok(User::from(result))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, Serialize)]
 | 
			
		||||
pub struct User {
 | 
			
		||||
    pub id: String,
 | 
			
		||||
    pub username: String,
 | 
			
		||||
    pub display_name: Option<String>,
 | 
			
		||||
    pub host: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<users::User> for User {
 | 
			
		||||
    fn from(u: users::User) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            id: u.id,
 | 
			
		||||
            username: u.username,
 | 
			
		||||
            display_name: u.display_name,
 | 
			
		||||
            host: u.host,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Into<users::User> for User {
 | 
			
		||||
    fn into(self) -> users::User {
 | 
			
		||||
        users::User {
 | 
			
		||||
            id: self.id,
 | 
			
		||||
            username: self.username,
 | 
			
		||||
            display_name: self.display_name,
 | 
			
		||||
            host: self.host,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub enum UserError {
 | 
			
		||||
    Duplicate,
 | 
			
		||||
    NotFound,
 | 
			
		||||
    Other(String),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<anyhow::Error> for UserError {
 | 
			
		||||
    fn from(err: anyhow::Error) -> Self {
 | 
			
		||||
        Self::Other(format!("UserError: {}", err))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<db::DBError> for UserError {
 | 
			
		||||
    fn from(err: db::DBError) -> Self {
 | 
			
		||||
        match err {
 | 
			
		||||
            db::DBError::Duplicate => Self::Duplicate,
 | 
			
		||||
            db::DBError::Other(e) => Self::Other(e.to_string()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToString for UserError {
 | 
			
		||||
    fn to_string(&self) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            Self::Duplicate => String::from("duplicate insert"),
 | 
			
		||||
            Self::NotFound => String::from("not found"),
 | 
			
		||||
            Self::Other(err) => err.clone(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Reject for UserError {}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
body {
 | 
			
		||||
	background-color: black;
 | 
			
		||||
	color: rebeccapurple;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
 | 
			
		||||
<head>
 | 
			
		||||
	<title>flabk - not found</title>
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
	<h1>not found</h1>
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
		Loading…
	
		Reference in New Issue