Compare commits

...

7 Commits

7 changed files with 131 additions and 47 deletions

View File

@ -128,6 +128,17 @@ docker run -p 4567:4567 \
-e site_url="https://www.example.com" \ -e site_url="https://www.example.com" \
-d chhoto-url:latest -d chhoto-url:latest
``` ```
1.c Optionally, set an API key to activate JSON result mode (optional)
```
docker run -p 4567:4567 \
-e password="password" \
-e api_key="SECURE_API_KEY" \
-v ./urls.sqlite:/urls.sqlite \
-e db_url=/urls.sqlite \
-e site_url="https://www.example.com" \
-d chhoto-url:latest
```
You can set the redirect method to Permanent 308 (default) or Temporary 307 by setting You can set the redirect method to Permanent 308 (default) or Temporary 307 by setting
the `redirect_method` variable to `TEMPORARY` or `PERMANENT` (it's matched exactly). By the `redirect_method` variable to `TEMPORARY` or `PERMANENT` (it's matched exactly). By
@ -148,6 +159,7 @@ served through a proxy.
The application can be used from the terminal using something like `curl`. In all the examples The application can be used from the terminal using something like `curl`. In all the examples
below, replace `http://localhost:4567` with where your instance of `chhoto-url` is accessible. below, replace `http://localhost:4567` with where your instance of `chhoto-url` is accessible.
### Cookie validation
If you have set up If you have set up
a password, first do the following to get an authentication cookie and store it in a file. a password, first do the following to get an authentication cookie and store it in a file.
```bash ```bash
@ -173,6 +185,33 @@ curl -X DELETE http://localhost:4567/api/del/<shortlink>
``` ```
The server will send a confirmation. The server will send a confirmation.
### API key validation
**This is required for programs that rely on a JSON response from Chhoto URL**
In order to use API key validation, set the `api_key` environment variable. If this is not set, the API will default to cookie validation (see section above).
If the API key is insecure, a warning will be outputted along with a generated API key which may be used.
To add a link:
``` bash
curl -X POST -H "Chhoto-Api-Key: <YOUR_API_KEY>" -d '{"shortlink":"<shortlink>", "longlink":"<longlink>"}' http://localhost:4567/api/new
```
To get a list of all the currently available links:
``` bash
curl -H "Chhoto-Api-Key: <YOUR_API_KEY>" http://localhost:4567/api/all
```
To delete a link:
``` bash
curl -X DELETE -H "Chhoto-Api-Key: <YOUR_API_KEY>" http://localhost:4567/api/del/<shortlink>
```
Where `<shortlink>` is name of the shortened link you would like to delete. For example, if the shortened link is `http://localhost:4567/example`, `<shortlink>` would be `example`.
The server will output when the instance is accessed over API, when an incorrect API key is received, etc.
In both modes, these routes are accessible:
You can get the version of `chhoto-url` the server is running using `curl http://localhost:4567/api/version` and You can get the version of `chhoto-url` the server is running using `curl http://localhost:4567/api/version` and
get the siteurl using `curl http://localhost:4567/api/siteurl`. get the siteurl using `curl http://localhost:4567/api/siteurl`.

80
actix/Cargo.lock generated
View File

@ -19,21 +19,6 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "actix-cors"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9e772b3bcafe335042b5db010ab7c09013dad6eac4915c91d8d50902769f331"
dependencies = [
"actix-utils",
"actix-web",
"derive_more 0.99.18",
"futures-util",
"log",
"once_cell",
"smallvec",
]
[[package]] [[package]]
name = "actix-files" name = "actix-files"
version = "0.6.6" version = "0.6.6"
@ -240,21 +225,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "actix-web-httpauth"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456348ed9dcd72a13a1f4a660449fafdecee9ac8205552e286809eb5b0b29bd3"
dependencies = [
"actix-utils",
"actix-web",
"base64 0.22.1",
"futures-core",
"futures-util",
"log",
"pin-project-lite",
]
[[package]] [[package]]
name = "addr2line" name = "addr2line"
version = "0.24.2" version = "0.24.2"
@ -508,13 +478,12 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
name = "chhoto-url" name = "chhoto-url"
version = "5.4.6" version = "5.4.6"
dependencies = [ dependencies = [
"actix-cors",
"actix-files", "actix-files",
"actix-session", "actix-session",
"actix-web", "actix-web",
"actix-web-httpauth",
"env_logger", "env_logger",
"nanoid", "nanoid",
"passwords",
"rand", "rand",
"regex", "regex",
"rusqlite", "rusqlite",
@ -768,7 +737,6 @@ dependencies = [
"futures-task", "futures-task",
"pin-project-lite", "pin-project-lite",
"pin-utils", "pin-utils",
"slab",
] ]
[[package]] [[package]]
@ -1260,6 +1228,15 @@ dependencies = [
"windows-targets", "windows-targets",
] ]
[[package]]
name = "passwords"
version = "3.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11407193a7c2bd14ec6b0ec3394da6fdcf7a4d5dcbc8c3cc38dfb17802c8d59c"
dependencies = [
"random-pick",
]
[[package]] [[package]]
name = "paste" name = "paste"
version = "1.0.15" version = "1.0.15"
@ -1317,6 +1294,12 @@ dependencies = [
"zerocopy", "zerocopy",
] ]
[[package]]
name = "proc-macro-hack"
version = "0.5.20+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.89" version = "1.0.89"
@ -1365,6 +1348,37 @@ dependencies = [
"getrandom", "getrandom",
] ]
[[package]]
name = "random-number"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fc8cdd49be664772ffc3dbfa743bb8c34b78f9cc6a9f50e56ae878546796067"
dependencies = [
"proc-macro-hack",
"rand",
"random-number-macro-impl",
]
[[package]]
name = "random-number-macro-impl"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5135143cb48d14289139e4615bffec0d59b4cbfd4ea2398a3770bd2abfc4aa2"
dependencies = [
"proc-macro-hack",
"quote",
"syn",
]
[[package]]
name = "random-pick"
version = "1.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c179499072da789afe44127d5f4aa6012de2c2f96ef759990196b37387a2a0f8"
dependencies = [
"random-number",
]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.5.7" version = "0.5.7"

View File

@ -29,8 +29,6 @@ categories = ["web-programming"]
[dependencies] [dependencies]
actix-web = "4.5.1" actix-web = "4.5.1"
actix-files = "0.6.5" actix-files = "0.6.5"
actix-cors = "0.7.0"
actix-web-httpauth = "0.8.2"
rusqlite = { version = "0.32.0", features = ["bundled"] } rusqlite = { version = "0.32.0", features = ["bundled"] }
regex = "1.10.3" regex = "1.10.3"
rand = "0.8.5" rand = "0.8.5"

View File

@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: 2023 Sayantan Santra <sayantan.santra689@gmail.com> // SPDX-FileCopyrightText: 2023 Sayantan Santra <sayantan.santra689@gmail.com>
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use rusqlite::{Connection}; use rusqlite::Connection;
use serde::Serialize; use serde::Serialize;
// Struct for encoding a DB row // Struct for encoding a DB row

View File

@ -4,7 +4,7 @@
use actix_files::NamedFile; use actix_files::NamedFile;
use actix_session::Session; use actix_session::Session;
use actix_web::{delete, get, http::StatusCode, post, web::{self, Redirect}, Either, HttpRequest, HttpResponse, Responder}; use actix_web::{delete, get, http::StatusCode, post, web::{self, Redirect}, Either, HttpRequest, HttpResponse, Responder};
use std::{env}; use std::env;
// Serialize JSON data // Serialize JSON data
use serde::Serialize; use serde::Serialize;
@ -25,6 +25,14 @@ struct Response {
reason: String, reason: String,
} }
// Needs to return the short URL to make it easier for programs leveraging the API
#[derive(Serialize)]
struct CreatedURL {
success: bool,
error: bool,
shorturl: String,
}
// Define the routes // Define the routes
// Add new links // Add new links
@ -36,9 +44,24 @@ pub async fn add_link(req: String, data: web::Data<AppState>, session: Session,
if result.success { if result.success {
let out = utils::add_link(req, &data.db); let out = utils::add_link(req, &data.db);
if out.0 { if out.0 {
HttpResponse::Created().body(out.1) let port = env::var("port")
.unwrap_or(String::from("4567"))
.parse::<u16>()
.expect("Supplied port is not an integer");
let url = format!("{}:{}", env::var("site_url").unwrap_or(String::from("http://localhost")), port);
let response = CreatedURL {
success: true,
error: false,
shorturl: format!("{}/{}", url, out.1)
};
HttpResponse::Created().json(response)
} else { } else {
HttpResponse::Conflict().body(out.1) let response = Response {
success: false,
error: true,
reason: out.1
};
HttpResponse::Conflict().json(response)
} }
} else if result.error { } else if result.error {
HttpResponse::Unauthorized().json(result) HttpResponse::Unauthorized().json(result)

View File

@ -18,6 +18,7 @@ struct URLPair {
} }
// Define JSON struct for response // Define JSON struct for response
// Named "ReturnResponse" rather than "Response" because of the previous import.
#[derive(Serialize)] #[derive(Serialize)]
pub struct Response { pub struct Response {
pub(crate) success: bool, pub(crate) success: bool,
@ -47,8 +48,14 @@ pub fn is_api_ok(http: HttpRequest) -> Response {
result result
} }
} else { } else {
let result = Response {success: false, error: false, reason: "".to_string(), pass: true}; // If the API key isn't set, but an API Key header is provided
result if let Some(_) = auth::api_header(&http) {
let result = Response {success: false, error: true, reason: "API key access was attempted, but no API key is configured".to_string(), pass: false};
result
} else {
let result = Response {success: false, error: false, reason: "".to_string(), pass: true};
result
}
} }
} }

View File

@ -3,7 +3,7 @@
services: services:
chhoto-url: chhoto-url:
image: chhoto-url image: sintan1729/chhoto-url:latest
restart: unless-stopped restart: unless-stopped
container_name: chhoto-url container_name: chhoto-url
ports: ports:
@ -24,8 +24,11 @@ services:
# - site_url=https://www.example.com # - site_url=https://www.example.com
- password=TopSecretPass - password=TopSecretPass
- api_key=test # This needs to be set in order to use programs that use the JSON interface of Chhoto URL.
# You will get a warning if this is insecure, and a generated value will be outputted
# You may use that value if you can't think of a secure key
# - api_key=SECURE_API_KEY
# Pass the redirect method, if needed. TEMPORARY and PERMANENT # Pass the redirect method, if needed. TEMPORARY and PERMANENT
# are accepted values, defaults to PERMANENT. # are accepted values, defaults to PERMANENT.
@ -35,12 +38,12 @@ services:
# If you want UIDs, please change slug_style to UID. # If you want UIDs, please change slug_style to UID.
# Supported values for slug_style are Pair and UID. # Supported values for slug_style are Pair and UID.
# The length is 8 by default, and a minimum of 4 is allowed. # The length is 8 by default, and a minimum of 4 is allowed.
- slug_style=Pair # - slug_style=Pair
- slug_length=8 # - slug_length=8
# In case you want to provide public access to adding links (and not # In case you want to provide public access to adding links (and not
# delete, or listing), change the following option to Enable. # delete, or listing), change the following option to Enable.
- public_mode=Disable # - public_mode=Disable
# By default, the server sends no Cache-Control headers. You can supply a # By default, the server sends no Cache-Control headers. You can supply a
# comma separated list of valid header as per RFC 7234 §5.2 to send those # comma separated list of valid header as per RFC 7234 §5.2 to send those