Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit bc71fc7

Browse files
committedNov 12, 2024·
init commit
1 parent 85a579f commit bc71fc7

File tree

17 files changed

+1512
-65
lines changed

17 files changed

+1512
-65
lines changed
 

‎.github/workflows/deploy.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
name: Deploy to GitHub Pages
2+
3+
permissions:
4+
contents: write
5+
pages: write
6+
7+
on:
8+
push:
9+
branches: [ "main", "master" ]
10+
workflow_dispatch:
11+
jobs:
12+
deploy:
13+
runs-on: ubuntu-latest
14+
steps: [uses: fastai/workflows/quarto-ghp@master]

‎.github/workflows/test.yaml.off

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
name: CI
2+
on: [workflow_dispatch, pull_request, push]
3+
4+
jobs:
5+
test:
6+
runs-on: ubuntu-latest
7+
steps: [uses: fastai/workflows/nbdev-ci@master]

‎.gitignore

Lines changed: 54 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
posts/
2+
.quarto
3+
.sesskey
4+
*.db-*
5+
*.db
6+
.gitattributes
7+
_proc/
8+
sidebar.yml
9+
Gemfile.lock
10+
token
11+
_docs/
12+
conda/
13+
.last_checked
14+
.gitconfig
15+
*.bak
16+
*.log
17+
*~
18+
~*
19+
_tmp*
20+
tmp*
21+
tags
22+
123
# Byte-compiled / optimized / DLL files
224
__pycache__/
325
*.py[cod]
@@ -8,6 +30,7 @@ __pycache__/
830

931
# Distribution / packaging
1032
.Python
33+
env/
1134
build/
1235
develop-eggs/
1336
dist/
@@ -20,11 +43,9 @@ parts/
2043
sdist/
2144
var/
2245
wheels/
23-
share/python-wheels/
2446
*.egg-info/
2547
.installed.cfg
2648
*.egg
27-
MANIFEST
2849

2950
# PyInstaller
3051
# Usually these files are written by a python script from a template
@@ -39,17 +60,13 @@ pip-delete-this-directory.txt
3960
# Unit test / coverage reports
4061
htmlcov/
4162
.tox/
42-
.nox/
4363
.coverage
4464
.coverage.*
4565
.cache
4666
nosetests.xml
4767
coverage.xml
4868
*.cover
49-
*.py,cover
5069
.hypothesis/
51-
.pytest_cache/
52-
cover/
5370

5471
# Translations
5572
*.mo
@@ -58,8 +75,6 @@ cover/
5875
# Django stuff:
5976
*.log
6077
local_settings.py
61-
db.sqlite3
62-
db.sqlite3-journal
6378

6479
# Flask stuff:
6580
instance/
@@ -72,63 +87,27 @@ instance/
7287
docs/_build/
7388

7489
# PyBuilder
75-
.pybuilder/
7690
target/
7791

7892
# Jupyter Notebook
7993
.ipynb_checkpoints
8094

81-
# IPython
82-
profile_default/
83-
ipython_config.py
84-
8595
# pyenv
86-
# For a library or package, you might want to ignore these files since the code is
87-
# intended to run in multiple environments; otherwise, check them in:
88-
# .python-version
89-
90-
# pipenv
91-
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92-
# However, in case of collaboration, if having platform-specific dependencies or dependencies
93-
# having no cross-platform support, pipenv may install dependencies that don't work, or not
94-
# install all needed dependencies.
95-
#Pipfile.lock
96-
97-
# poetry
98-
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99-
# This is especially recommended for binary packages to ensure reproducibility, and is more
100-
# commonly ignored for libraries.
101-
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102-
#poetry.lock
103-
104-
# pdm
105-
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106-
#pdm.lock
107-
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108-
# in version control.
109-
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
110-
.pdm.toml
111-
.pdm-python
112-
.pdm-build/
113-
114-
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
115-
__pypackages__/
116-
117-
# Celery stuff
96+
.python-version
97+
98+
# celery beat schedule file
11899
celerybeat-schedule
119-
celerybeat.pid
120100

121101
# SageMath parsed files
122102
*.sage.py
123103

124-
# Environments
104+
# dotenv
125105
.env
106+
107+
# virtualenv
126108
.venv
127-
env/
128109
venv/
129110
ENV/
130-
env.bak/
131-
venv.bak/
132111

133112
# Spyder project settings
134113
.spyderproject
@@ -142,21 +121,32 @@ venv.bak/
142121

143122
# mypy
144123
.mypy_cache/
145-
.dmypy.json
146-
dmypy.json
147124

148-
# Pyre type checker
149-
.pyre/
125+
.vscode
126+
*.swp
127+
128+
# osx generated files
129+
.DS_Store
130+
.DS_Store?
131+
.Trashes
132+
ehthumbs.db
133+
Thumbs.db
134+
.idea
135+
136+
# pytest
137+
.pytest_cache
138+
139+
# tools/trust-doc-nbs
140+
docs_src/.last_checked
141+
142+
# symlinks to fastai
143+
docs_src/fastai
144+
tools/fastai
150145

151-
# pytype static type analyzer
152-
.pytype/
146+
# link checker
147+
checklink/cookies.txt
153148

154-
# Cython debug symbols
155-
cython_debug/
149+
# .gitconfig is now autogenerated
150+
.gitconfig
156151

157-
# PyCharm
158-
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
159-
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
160-
# and can be added to the global gitignore or merged into this file. For a more nuclear
161-
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
162-
#.idea/
152+
_docs

‎MANIFEST.in

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
include settings.ini
2+
include LICENSE
3+
include CONTRIBUTING.md
4+
include README.md
5+
recursive-exclude * __pycache__

‎README.md

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,67 @@
11
# fastcaddy
2-
A simple python wrapper for using the Caddy API
2+
3+
4+
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
5+
6+
This file will become your README and also the index of your
7+
documentation.
8+
9+
## Developer Guide
10+
11+
If you are new to using `nbdev` here are some useful pointers to get you
12+
started.
13+
14+
### Install fastcaddy in Development mode
15+
16+
``` sh
17+
# make sure fastcaddy package is installed in development mode
18+
$ pip install -e .
19+
20+
# make changes under nbs/ directory
21+
# ...
22+
23+
# compile to have changes apply to fastcaddy
24+
$ nbdev_prepare
25+
```
26+
27+
## Usage
28+
29+
### Installation
30+
31+
Install latest from the GitHub
32+
[repository](https://github.com/AnswerDotAI/fastcaddy):
33+
34+
``` sh
35+
$ pip install git+https://github.com/AnswerDotAI/fastcaddy.git
36+
```
37+
38+
or from [conda](https://anaconda.org/AnswerDotAI/fastcaddy)
39+
40+
``` sh
41+
$ conda install -c AnswerDotAI fastcaddy
42+
```
43+
44+
or from [pypi](https://pypi.org/project/fastcaddy/)
45+
46+
``` sh
47+
$ pip install fastcaddy
48+
```
49+
50+
### Documentation
51+
52+
Documentation can be found hosted on this GitHub
53+
[repository](https://github.com/AnswerDotAI/fastcaddy)’s
54+
[pages](https://AnswerDotAI.github.io/fastcaddy/). Additionally you can
55+
find package manager specific guidelines on
56+
[conda](https://anaconda.org/AnswerDotAI/fastcaddy) and
57+
[pypi](https://pypi.org/project/fastcaddy/) respectively.
58+
59+
## How to use
60+
61+
Fill me in please! Don’t forget code examples:
62+
63+
``` python
64+
1+1
65+
```
66+
67+
2

‎fastcaddy/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__version__ = "0.0.1"

‎fastcaddy/_modidx.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Autogenerated by nbdev
2+
3+
d = { 'settings': { 'branch': 'main',
4+
'doc_baseurl': '/fastcaddy',
5+
'doc_host': 'https://AnswerDotAI.github.io',
6+
'git_url': 'https://github.com/AnswerDotAI/fastcaddy',
7+
'lib_path': 'fastcaddy'},
8+
'syms': { 'fastcaddy.core': { 'fastcaddy.core.add_acme_config': ('core.html#add_acme_config', 'fastcaddy/core.py'),
9+
'fastcaddy.core.add_reverse_proxy': ('core.html#add_reverse_proxy', 'fastcaddy/core.py'),
10+
'fastcaddy.core.add_route': ('core.html#add_route', 'fastcaddy/core.py'),
11+
'fastcaddy.core.del_id': ('core.html#del_id', 'fastcaddy/core.py'),
12+
'fastcaddy.core.gcfg': ('core.html#gcfg', 'fastcaddy/core.py'),
13+
'fastcaddy.core.get_acme_config': ('core.html#get_acme_config', 'fastcaddy/core.py'),
14+
'fastcaddy.core.get_id': ('core.html#get_id', 'fastcaddy/core.py'),
15+
'fastcaddy.core.get_path': ('core.html#get_path', 'fastcaddy/core.py'),
16+
'fastcaddy.core.gid': ('core.html#gid', 'fastcaddy/core.py'),
17+
'fastcaddy.core.init_path': ('core.html#init_path', 'fastcaddy/core.py'),
18+
'fastcaddy.core.init_routes': ('core.html#init_routes', 'fastcaddy/core.py'),
19+
'fastcaddy.core.keys2path': ('core.html#keys2path', 'fastcaddy/core.py'),
20+
'fastcaddy.core.nested_setcfg': ('core.html#nested_setcfg', 'fastcaddy/core.py'),
21+
'fastcaddy.core.nested_setdict': ('core.html#nested_setdict', 'fastcaddy/core.py'),
22+
'fastcaddy.core.path2keys': ('core.html#path2keys', 'fastcaddy/core.py'),
23+
'fastcaddy.core.pcfg': ('core.html#pcfg', 'fastcaddy/core.py'),
24+
'fastcaddy.core.pid': ('core.html#pid', 'fastcaddy/core.py')}}}

‎fastcaddy/core.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
"""Fill in a module description here"""
2+
3+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_core.ipynb.
4+
5+
# %% auto 0
6+
__all__ = ['acme_path', 'srvs_path', 'rts_path', 'get_id', 'get_path', 'gid', 'gcfg', 'pid', 'pcfg', 'nested_setdict',
7+
'path2keys', 'keys2path', 'nested_setcfg', 'init_path', 'get_acme_config', 'add_acme_config', 'init_routes',
8+
'add_route', 'add_reverse_proxy', 'del_id']
9+
10+
# %% ../nbs/00_core.ipynb 4
11+
import os, subprocess, httpx, json
12+
from fastcore.utils import *
13+
from httpx import get as xget, post as xpost, patch as xpatch, put as xput, delete as xdelete, head as xhead
14+
15+
# %% ../nbs/00_core.ipynb 6
16+
def get_id(path):
17+
"Get a ID full URL from a path"
18+
if path[0 ]!='/': path = '/'+path
19+
if path[-1]!='/': path = path+'/'
20+
return f'http://localhost:2019/id{path}'
21+
22+
# %% ../nbs/00_core.ipynb 7
23+
def get_path(path):
24+
"Get a config full URL from a path"
25+
if path[0 ]!='/': path = '/'+path
26+
if path[-1]!='/': path = path+'/'
27+
return f'http://localhost:2019/config{path}'
28+
29+
# %% ../nbs/00_core.ipynb 9
30+
def gid(path='/'):
31+
"Gets the id at `path`"
32+
response = xget(get_id(path))
33+
response.raise_for_status()
34+
return dict2obj(response.json())
35+
36+
# %% ../nbs/00_core.ipynb 10
37+
def gcfg(path='/', method='get'):
38+
"Gets the config at `path`"
39+
f = getattr(httpx, method)
40+
response = f(get_path(path))
41+
response.raise_for_status()
42+
return dict2obj(response.json())
43+
44+
# %% ../nbs/00_core.ipynb 12
45+
def pid(d, path='/', method='post'):
46+
"Puts the config `d` into `path`"
47+
f = getattr(httpx, method)
48+
response = f(get_id(path), json=obj2dict(d))
49+
response.raise_for_status()
50+
return response.text or None
51+
52+
# %% ../nbs/00_core.ipynb 13
53+
def pcfg(d, path='/', method='post'):
54+
"Puts the config `d` into `path`"
55+
f = getattr(httpx, method)
56+
response = f(get_path(path), json=obj2dict(d))
57+
response.raise_for_status()
58+
return response.text or None
59+
60+
# %% ../nbs/00_core.ipynb 15
61+
def nested_setdict(sd, value, *keys):
62+
"Returns `sd` updated to set `value` at the path `keys`"
63+
d = sd
64+
for key in keys[:-1]: d = d.setdefault(key, {})
65+
d[keys[-1]] = value
66+
return sd
67+
68+
# %% ../nbs/00_core.ipynb 17
69+
def path2keys(path):
70+
"Split `path` by '/' into a list"
71+
return path.strip('/').split('/')
72+
73+
# %% ../nbs/00_core.ipynb 19
74+
def keys2path(*keys):
75+
"Join `keys` into a '/' separated path"
76+
return '/'+'/'.join(keys)
77+
78+
# %% ../nbs/00_core.ipynb 21
79+
def nested_setcfg(value, *keys):
80+
d = nested_setdict(gcfg(), value, *keys)
81+
return pcfg(d)
82+
83+
# %% ../nbs/00_core.ipynb 22
84+
def init_path(path):
85+
sp = []
86+
for p in path2keys(path):
87+
sp.append(p)
88+
pcfg({}, keys2path(*sp))
89+
90+
# %% ../nbs/00_core.ipynb 25
91+
acme_path = '/apps/tls/automation'
92+
def get_acme_config(token):
93+
prov = { "provider": { "name": "cloudflare", "api_token": token } }
94+
return {
95+
"module": "acme",
96+
"challenges": { "dns": prov }
97+
}
98+
99+
# %% ../nbs/00_core.ipynb 26
100+
def add_acme_config(cf_token):
101+
init_path(acme_path)
102+
val = [get_acme_config(cf_token)]
103+
pcfg([{'issuers':val}], acme_path+'/policies')
104+
105+
# %% ../nbs/00_core.ipynb 31
106+
srvs_path = '/apps/http/servers'
107+
rts_path = srvs_path+'/srv0/routes'
108+
109+
# %% ../nbs/00_core.ipynb 32
110+
def init_routes(srv_name='srv0'):
111+
# Create basic http server/routes config
112+
init_path(srvs_path)
113+
ir = {'listen': [':80', ':443'], 'routes': []}
114+
pcfg(ir, f"{srvs_path}/{srv_name}")
115+
116+
# %% ../nbs/00_core.ipynb 35
117+
def add_route(route):
118+
"Add `route` dict to config"
119+
return pcfg(route, rts_path)
120+
121+
# %% ../nbs/00_core.ipynb 36
122+
def add_reverse_proxy(from_host, to_url):
123+
"Create a reverse proxy handler"
124+
route = {
125+
"handle": [{
126+
"handler": "reverse_proxy",
127+
"upstreams": [{"dial": to_url}]
128+
}],
129+
"match": [{"host": [from_host]}],
130+
"@id": from_host,
131+
"terminal": True
132+
}
133+
add_route(route)
134+
135+
# %% ../nbs/00_core.ipynb 41
136+
def del_id(id):
137+
"Delete route for `id` (e.g. a host)"
138+
xdelete(get_id(id))

‎nbs/00_core.ipynb

Lines changed: 663 additions & 0 deletions
Large diffs are not rendered by default.

‎nbs/Untitled.ipynb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"id": "1527e0e0",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": []
10+
}
11+
],
12+
"metadata": {
13+
"kernelspec": {
14+
"display_name": "python3",
15+
"language": "python",
16+
"name": "python3"
17+
}
18+
},
19+
"nbformat": 4,
20+
"nbformat_minor": 5
21+
}

‎nbs/_quarto.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
project:
2+
type: website
3+
4+
format:
5+
html:
6+
theme: cosmo
7+
css: styles.css
8+
toc: true
9+
keep-md: true
10+
commonmark: default
11+
12+
website:
13+
twitter-card: true
14+
open-graph: true
15+
repo-actions: [issue]
16+
navbar:
17+
background: primary
18+
search: true
19+
sidebar:
20+
style: floating
21+
22+
metadata-files: [nbdev.yml, sidebar.yml]

‎nbs/index.ipynb

Lines changed: 339 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,339 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"metadata": {},
7+
"outputs": [],
8+
"source": [
9+
"#| hide\n",
10+
"from fastcaddy.core import *"
11+
]
12+
},
13+
{
14+
"cell_type": "markdown",
15+
"metadata": {},
16+
"source": [
17+
"# fastcaddy\n",
18+
"\n",
19+
"> A simple python wrapper for using the caddy API"
20+
]
21+
},
22+
{
23+
"cell_type": "markdown",
24+
"metadata": {},
25+
"source": [
26+
"## Usage"
27+
]
28+
},
29+
{
30+
"cell_type": "markdown",
31+
"metadata": {},
32+
"source": [
33+
"### Installation"
34+
]
35+
},
36+
{
37+
"cell_type": "markdown",
38+
"metadata": {},
39+
"source": [
40+
"Install from [pypi][pypi]\n",
41+
"\n",
42+
"\n",
43+
"```sh\n",
44+
"$ pip install fastcaddy\n",
45+
"```\n",
46+
"\n",
47+
"[pypi]: https://pypi.org/project/fastcaddy/"
48+
]
49+
},
50+
{
51+
"cell_type": "markdown",
52+
"metadata": {},
53+
"source": [
54+
"## Installing Caddy"
55+
]
56+
},
57+
{
58+
"cell_type": "code",
59+
"execution_count": null,
60+
"metadata": {},
61+
"outputs": [],
62+
"source": [
63+
"from fastcore.utils import *"
64+
]
65+
},
66+
{
67+
"cell_type": "markdown",
68+
"metadata": {},
69+
"source": [
70+
"This project is to help you use the caddy API, rather than a Caddyfile, to use caddy. To use the API, you need to install a plugin for your domain management service. We use Cloudflare, so we'll document that here. For other domain services, see the Caddy docs for other plugins."
71+
]
72+
},
73+
{
74+
"cell_type": "markdown",
75+
"metadata": {},
76+
"source": [
77+
"### Cloudflare setup"
78+
]
79+
},
80+
{
81+
"cell_type": "markdown",
82+
"metadata": {},
83+
"source": [
84+
"You'll need a token from Cloudflare with access to modify the necessary settings. Here's the steps to create a token with the minimal privileges. You'll need to install the cloudflare pip package, then import:"
85+
]
86+
},
87+
{
88+
"cell_type": "code",
89+
"execution_count": null,
90+
"metadata": {},
91+
"outputs": [],
92+
"source": [
93+
"from cloudflare import Cloudflare"
94+
]
95+
},
96+
{
97+
"cell_type": "markdown",
98+
"metadata": {},
99+
"source": [
100+
"Then you'll need create a Cloudflare API token for your user, which we'll then use to create the less privileged token."
101+
]
102+
},
103+
{
104+
"cell_type": "code",
105+
"execution_count": null,
106+
"metadata": {},
107+
"outputs": [],
108+
"source": [
109+
"cf_token = os.environ['CLOUDFLARE_API_TOKEN']"
110+
]
111+
},
112+
{
113+
"cell_type": "markdown",
114+
"metadata": {},
115+
"source": [
116+
"We can now check that works OK:"
117+
]
118+
},
119+
{
120+
"cell_type": "code",
121+
"execution_count": null,
122+
"metadata": {},
123+
"outputs": [
124+
{
125+
"data": {
126+
"text/plain": [
127+
"8"
128+
]
129+
},
130+
"execution_count": null,
131+
"metadata": {},
132+
"output_type": "execute_result"
133+
}
134+
],
135+
"source": [
136+
"cf = Cloudflare(api_token=cf_token)\n",
137+
"zones = cf.zones.list()\n",
138+
"len(zones.result)"
139+
]
140+
},
141+
{
142+
"cell_type": "markdown",
143+
"metadata": {},
144+
"source": [
145+
"Replace this with your domain name:"
146+
]
147+
},
148+
{
149+
"cell_type": "code",
150+
"execution_count": null,
151+
"metadata": {},
152+
"outputs": [],
153+
"source": [
154+
"domain = 'answer.ai'\n",
155+
"zones = cf.zones.list(name=domain)\n",
156+
"assert len(zones.result)==1"
157+
]
158+
},
159+
{
160+
"cell_type": "code",
161+
"execution_count": null,
162+
"metadata": {},
163+
"outputs": [],
164+
"source": [
165+
"zone_id = zones.result[0].id"
166+
]
167+
},
168+
{
169+
"cell_type": "markdown",
170+
"metadata": {},
171+
"source": [
172+
"Here's the methods available for modifying DNS records:\n",
173+
"\n",
174+
"- `client.dns.records.create(*, zone_id, **params) -> Optional`\n",
175+
"- `client.dns.records.update(dns_record_id, *, zone_id, **params) -> Optional`\n",
176+
"- `client.dns.records.list(*, zone_id, **params) -> SyncV4PagePaginationArray[Record]`\n",
177+
"- `client.dns.records.delete(dns_record_id, *, zone_id) -> Optional`\n",
178+
"- `client.dns.records.edit(dns_record_id, *, zone_id, **params) -> Optional`\n",
179+
"- `client.dns.records.export(*, zone_id) -> str`\n",
180+
"- `client.dns.records.get(dns_record_id, *, zone_id) -> Optional`\n",
181+
"- `client.dns.records.import\\_(*, zone_id, **params) -> Optional`\n",
182+
"- `client.dns.records.scan(*, zone_id, **params) -> Optional`"
183+
]
184+
},
185+
{
186+
"cell_type": "markdown",
187+
"metadata": {},
188+
"source": [
189+
"…and here's the methods for tokens:\n",
190+
"\n",
191+
"```python\n",
192+
"from cloudflare.types.user import (CIDRList, Policy, Token, TokenCreateResponse, TokenUpdateResponse, TokenListResponse,\n",
193+
" TokenDeleteResponse, TokenGetResponse, TokenVerifyResponse)\n",
194+
"```\n",
195+
"\n",
196+
"- `client.user.tokens.create(**params) -> Optional`\n",
197+
"- `client.user.tokens.update(token_id, **params) -> object`\n",
198+
"- `client.user.tokens.list(**params) -> SyncV4PagePaginationArray[object]`\n",
199+
"- `client.user.tokens.delete(token_id) -> Optional`\n",
200+
"- `client.user.tokens.get(token_id) -> object`\n",
201+
"- `client.user.tokens.verify() -> Optional`\n",
202+
"\n",
203+
"```python\n",
204+
"from cloudflare.types.user.tokens import PermissionGroupListResponse\n",
205+
"```\n",
206+
"\n",
207+
"- client.user.tokens.permission_groups.list() -> SyncSinglePage[object]\n",
208+
"\n",
209+
"```python\n",
210+
"from cloudflare.types.user.tokens import Value\n",
211+
"```\n",
212+
"\n",
213+
"- client.user.tokens.value.update(token_id, **params) -> str"
214+
]
215+
},
216+
{
217+
"cell_type": "markdown",
218+
"metadata": {},
219+
"source": [
220+
"We need these two permissions in our token:"
221+
]
222+
},
223+
{
224+
"cell_type": "code",
225+
"execution_count": null,
226+
"metadata": {},
227+
"outputs": [],
228+
"source": [
229+
"permission_groups = cf.user.tokens.permission_groups.list()\n",
230+
"\n",
231+
"dns_write = next(group for group in permission_groups if group['name'] == 'DNS Write')\n",
232+
"zone_read = next(group for group in permission_groups if group['name'] == 'Zone Read')"
233+
]
234+
},
235+
{
236+
"cell_type": "markdown",
237+
"metadata": {},
238+
"source": [
239+
"Now we can create it:\n",
240+
"\n",
241+
"```python\n",
242+
"new_token = cf.user.tokens.create(\n",
243+
" name='caddy_dns',\n",
244+
" policies=[{\n",
245+
" \"effect\": \"allow\",\n",
246+
" \"resources\": { f\"com.cloudflare.api.account.zone.{zone_id}\": \"*\" },\n",
247+
" \"permission_groups\": [\n",
248+
" {\"id\": zone_read['id'], \"name\": \"Zone Read\"},\n",
249+
" {\"id\": dns_write['id'], \"name\": \"DNS Write\"}\n",
250+
" ]\n",
251+
" }]\n",
252+
")\n",
253+
"\n",
254+
"print(new_token.value)\n",
255+
"```"
256+
]
257+
},
258+
{
259+
"cell_type": "markdown",
260+
"metadata": {},
261+
"source": [
262+
"Make a copy of this value, which we'll need for setting up caddy."
263+
]
264+
},
265+
{
266+
"cell_type": "markdown",
267+
"metadata": {},
268+
"source": [
269+
"### Installing caddy"
270+
]
271+
},
272+
{
273+
"cell_type": "markdown",
274+
"metadata": {},
275+
"source": [
276+
"To install caddy, we'll use a tool called `xcaddy`. This is written in go. So first install go:\n",
277+
"\n",
278+
"- Mac: `brew install go`\n",
279+
"- Linux: `sudo apt install golang`\n",
280+
"\n",
281+
"Now we can install xcaddy:\n",
282+
"\n",
283+
"```sh\n",
284+
"go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest\n",
285+
"```\n",
286+
"\n",
287+
"Then we use that to compile caddy with our desired domain plugin (cloudflare, in this case):\n",
288+
"\n",
289+
"```sh\n",
290+
"cd ~/go/bin\n",
291+
"./xcaddy build --with github.com/caddy-dns/cloudflare\n",
292+
"```\n",
293+
"\n",
294+
"This gives us a `~/go/bin/caddy` binary we can run:\n",
295+
"\n",
296+
"```sh\n",
297+
"./caddy run\n",
298+
"```"
299+
]
300+
},
301+
{
302+
"cell_type": "markdown",
303+
"metadata": {},
304+
"source": [
305+
"### Auto-run caddy on start"
306+
]
307+
},
308+
{
309+
"cell_type": "markdown",
310+
"metadata": {},
311+
"source": [
312+
"If you're using a server or running caddy a lot, you'll want it to run on start. This isn't needed otherwise -- you can just `~/go/bin/caddy run` to run it manually (you may want to add `~/go/bin` to your `PATH` env var)."
313+
]
314+
},
315+
{
316+
"cell_type": "markdown",
317+
"metadata": {},
318+
"source": [
319+
"## How to use"
320+
]
321+
},
322+
{
323+
"cell_type": "code",
324+
"execution_count": null,
325+
"metadata": {},
326+
"outputs": [],
327+
"source": []
328+
}
329+
],
330+
"metadata": {
331+
"kernelspec": {
332+
"display_name": "python3",
333+
"language": "python",
334+
"name": "python3"
335+
}
336+
},
337+
"nbformat": 4,
338+
"nbformat_minor": 4
339+
}

‎nbs/nbdev.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
project:
2+
output-dir: _docs
3+
4+
website:
5+
title: "fastcaddy"
6+
site-url: "https://AnswerDotAI.github.io/fastcaddy"
7+
description: "A simple python wrapper for using the Caddy API"
8+
repo-branch: main
9+
repo-url: "https://github.com/AnswerDotAI/fastcaddy"

‎nbs/styles.css

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
.cell {
2+
margin-bottom: 1rem;
3+
}
4+
5+
.cell > .sourceCode {
6+
margin-bottom: 0;
7+
}
8+
9+
.cell-output > pre {
10+
margin-bottom: 0;
11+
}
12+
13+
.cell-output > pre, .cell-output > .sourceCode > pre, .cell-output-stdout > pre {
14+
margin-left: 0.8rem;
15+
margin-top: 0;
16+
background: none;
17+
border-left: 2px solid lightsalmon;
18+
border-top-left-radius: 0;
19+
border-top-right-radius: 0;
20+
}
21+
22+
.cell-output > .sourceCode {
23+
border: none;
24+
}
25+
26+
.cell-output > .sourceCode {
27+
background: none;
28+
margin-top: 0;
29+
}
30+
31+
div.description {
32+
padding-left: 2px;
33+
padding-top: 5px;
34+
font-style: italic;
35+
font-size: 135%;
36+
opacity: 70%;
37+
}

‎pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[build-system]
2+
requires = ["setuptools>=64.0"]
3+
build-backend = "setuptools.build_meta"

‎settings.ini

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
[DEFAULT]
2+
# All sections below are required unless otherwise specified.
3+
# See https://github.com/fastai/nbdev/blob/master/settings.ini for examples.
4+
5+
### Python library ###
6+
repo = fastcaddy
7+
lib_name = %(repo)s
8+
version = 0.0.1
9+
min_python = 3.7
10+
license = apache2
11+
black_formatting = False
12+
13+
### nbdev ###
14+
doc_path = _docs
15+
lib_path = fastcaddy
16+
nbs_path = nbs
17+
recursive = True
18+
tst_flags = notest
19+
put_version_in_init = True
20+
21+
### Docs ###
22+
branch = main
23+
custom_sidebar = False
24+
doc_host = https://%(user)s.github.io
25+
doc_baseurl = /%(repo)s
26+
git_url = https://github.com/%(user)s/%(repo)s
27+
title = %(lib_name)s
28+
29+
### PyPI ###
30+
audience = Developers
31+
author = Jeremy Howard
32+
author_email = github@jhoward.fastmail.fm
33+
copyright = 2024 onwards, %(author)s
34+
description = A simple python wrapper for using the Caddy API
35+
keywords = nbdev jupyter notebook python
36+
language = English
37+
status = 3
38+
user = AnswerDotAI
39+
40+
### Optional ###
41+
# requirements = fastcore pandas
42+
# dev_requirements =
43+
# console_scripts =
44+
# conda_user =
45+
# package_data =

‎setup.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from pkg_resources import parse_version
2+
from configparser import ConfigParser
3+
import setuptools, shlex
4+
assert parse_version(setuptools.__version__)>=parse_version('36.2')
5+
6+
# note: all settings are in settings.ini; edit there, not here
7+
config = ConfigParser(delimiters=['='])
8+
config.read('settings.ini', encoding='utf-8')
9+
cfg = config['DEFAULT']
10+
11+
cfg_keys = 'version description keywords author author_email'.split()
12+
expected = cfg_keys + "lib_name user branch license status min_python audience language".split()
13+
for o in expected: assert o in cfg, "missing expected setting: {}".format(o)
14+
setup_cfg = {o:cfg[o] for o in cfg_keys}
15+
16+
licenses = {
17+
'apache2': ('Apache Software License 2.0','OSI Approved :: Apache Software License'),
18+
'mit': ('MIT License', 'OSI Approved :: MIT License'),
19+
'gpl2': ('GNU General Public License v2', 'OSI Approved :: GNU General Public License v2 (GPLv2)'),
20+
'gpl3': ('GNU General Public License v3', 'OSI Approved :: GNU General Public License v3 (GPLv3)'),
21+
'bsd3': ('BSD License', 'OSI Approved :: BSD License'),
22+
}
23+
statuses = [ '1 - Planning', '2 - Pre-Alpha', '3 - Alpha',
24+
'4 - Beta', '5 - Production/Stable', '6 - Mature', '7 - Inactive' ]
25+
py_versions = '3.6 3.7 3.8 3.9 3.10 3.11 3.12'.split()
26+
27+
requirements = shlex.split(cfg.get('requirements', ''))
28+
if cfg.get('pip_requirements'): requirements += shlex.split(cfg.get('pip_requirements', ''))
29+
min_python = cfg['min_python']
30+
lic = licenses.get(cfg['license'].lower(), (cfg['license'], None))
31+
dev_requirements = (cfg.get('dev_requirements') or '').split()
32+
33+
package_data = dict()
34+
pkg_data = cfg.get('package_data', None)
35+
if pkg_data:
36+
package_data[cfg['lib_name']] = pkg_data.split() # split as multiple files might be listed
37+
# Add package data to setup_cfg for setuptools.setup(..., **setup_cfg)
38+
setup_cfg['package_data'] = package_data
39+
40+
setuptools.setup(
41+
name = cfg['lib_name'],
42+
license = lic[0],
43+
classifiers = [
44+
'Development Status :: ' + statuses[int(cfg['status'])],
45+
'Intended Audience :: ' + cfg['audience'].title(),
46+
'Natural Language :: ' + cfg['language'].title(),
47+
] + ['Programming Language :: Python :: '+o for o in py_versions[py_versions.index(min_python):]] + (['License :: ' + lic[1] ] if lic[1] else []),
48+
url = cfg['git_url'],
49+
packages = setuptools.find_packages(),
50+
include_package_data = True,
51+
install_requires = requirements,
52+
extras_require={ 'dev': dev_requirements },
53+
dependency_links = cfg.get('dep_links','').split(),
54+
python_requires = '>=' + cfg['min_python'],
55+
long_description = open('README.md', encoding='utf-8').read(),
56+
long_description_content_type = 'text/markdown',
57+
zip_safe = False,
58+
entry_points = {
59+
'console_scripts': cfg.get('console_scripts','').split(),
60+
'nbdev': [f'{cfg.get("lib_path")}={cfg.get("lib_path")}._modidx:d']
61+
},
62+
**setup_cfg)
63+
64+

0 commit comments

Comments
 (0)
Please sign in to comment.