feat: add python example server
This commit is contained in:
commit
51802bc4f8
7 changed files with 343 additions and 0 deletions
133
.gitignore
vendored
Normal file
133
.gitignore
vendored
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
tmp/
|
3
python/.env_sample
Normal file
3
python/.env_sample
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
SITEKEY=
|
||||||
|
SECRET=
|
||||||
|
INSTANCE_URL=
|
1
python/.gitignore
vendored
Normal file
1
python/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
__pycache__/
|
40
python/README.md
Normal file
40
python/README.md
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
# Example Server with mCaptcha protection
|
||||||
|
|
||||||
|
The example server shows a dummy form with the mCaptcha widget. When the
|
||||||
|
form is submitted, it validates the [authorization
|
||||||
|
token](https://mcaptcha.org/docs/webmasters/terminology#authorization-token)
|
||||||
|
presented by the visitor against the mCaptcha instance that the server
|
||||||
|
is configured with.
|
||||||
|
|
||||||
|
The example server is built with the Flask webframework and uses the
|
||||||
|
[Python API library for mCaptcha](https://pypi.org/project/mcaptcha-api/0.1.0/) for validation.
|
||||||
|
|
||||||
|
## 1. Configuration
|
||||||
|
|
||||||
|
Before running, please configure the server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env_sample .env
|
||||||
|
```
|
||||||
|
|
||||||
|
And fill in the configuration parameters in `.env` file with:
|
||||||
|
|
||||||
|
1. [Sitekey](https://mcaptcha.org/docs/webmasters/terminology#sitekey)
|
||||||
|
2. Account secret: Available in the settings page on the mCaptcha
|
||||||
|
dashboard
|
||||||
|
3. Instance URL
|
||||||
|
|
||||||
|
## 2. Install dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
virtualenv venv && . venv/bin/activate && pip install -r ./requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Launch server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
flask --app server run
|
||||||
|
```
|
||||||
|
|
||||||
|
If all configuration parameters are properly filled in, the example server must
|
||||||
|
work.
|
13
python/requirements.txt
Normal file
13
python/requirements.txt
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
blinker==1.7.0
|
||||||
|
certifi==2023.11.17
|
||||||
|
charset-normalizer==3.3.2
|
||||||
|
click==8.1.7
|
||||||
|
Flask==3.0.0
|
||||||
|
idna==3.6
|
||||||
|
itsdangerous==2.1.2
|
||||||
|
Jinja2==3.1.2
|
||||||
|
MarkupSafe==2.1.3
|
||||||
|
mcaptcha_api==0.1.0
|
||||||
|
requests==2.31.0
|
||||||
|
urllib3==2.1.0
|
||||||
|
Werkzeug==3.0.1
|
43
python/server.py
Normal file
43
python/server.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import os
|
||||||
|
from flask import Flask, render_template, request
|
||||||
|
from mcaptcha_api import MCaptcha
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
sitekey = os.environ["SITEKEY"]
|
||||||
|
secret = os.environ["SECRET"]
|
||||||
|
instance_url = os.environ["INSTANCE_URL"]
|
||||||
|
|
||||||
|
if len(sitekey) == 0:
|
||||||
|
print("please enter sitekey")
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
|
||||||
|
if len(secret) == 0:
|
||||||
|
print("please enter secret")
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
|
||||||
|
if len(instance_url) == 0:
|
||||||
|
print("please enter instance url")
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def index():
|
||||||
|
return render_template(
|
||||||
|
"index.html.j2", widget_link=f"{instance_url}/widget?sitekey={sitekey}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
mcaptcha = MCaptcha(instance_url=instance_url, sitekey=sitekey, secret=secret)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/login", methods=["POST"])
|
||||||
|
def login_page():
|
||||||
|
username = request.form["username"]
|
||||||
|
token = request.form["mcaptcha__token"]
|
||||||
|
if mcaptcha.verify(token=token):
|
||||||
|
return "<p> Success. Click <a href="/">here</a> to retry</p>"
|
||||||
|
else:
|
||||||
|
return '<p> Failed. Click <a href="/">here</a> to retry</p>'
|
110
python/templates/index.html.j2
Normal file
110
python/templates/index.html.j2
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Home | mCaptcha Example SErver</title>
|
||||||
|
|
||||||
|
<meta name="referrer" content="no-referrer-when-downgrade" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="mCaptcha is an AGPL'd, privacy focued, proof-of-work based CAPTCHA System"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<form action="/" method="post">
|
||||||
|
<h1>Demo Form</h1>
|
||||||
|
<label for="name">
|
||||||
|
Name
|
||||||
|
<input type="text" name="name" id="name" />
|
||||||
|
</label>
|
||||||
|
<label for="comment">
|
||||||
|
Comment
|
||||||
|
<input type="password" name="comment" id="comment" />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label
|
||||||
|
for="mcaptcha__token"
|
||||||
|
data-mcaptcha_url="{{ widget_link }}"
|
||||||
|
id="mcaptcha__token-label"
|
||||||
|
>
|
||||||
|
mCaptcha authorization token.
|
||||||
|
<a
|
||||||
|
href="https://mcaptcha.org/docs/user-manual/how-to-mcaptcha-without-js/"
|
||||||
|
>Instructions</a
|
||||||
|
>.
|
||||||
|
<input type="text" name="mcaptcha__token" id="mcaptcha__token" />
|
||||||
|
</label>
|
||||||
|
<div id="mcaptcha__widget-container"></div>
|
||||||
|
<script src="https://unpkg.com/@mcaptcha/vanilla-glue@0.1.0-rc2/dist/index.js"></script>
|
||||||
|
|
||||||
|
<button type="submit">Register</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
margin: 5px 0 auto;
|
||||||
|
height: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
#mcaptcha__token {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
background: green;
|
||||||
|
color: #fff;
|
||||||
|
height: 26px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 5px 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
align-self: center;
|
||||||
|
margin: 40px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mcaptcha__widget-container {
|
||||||
|
height: 78px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue