Update documentation with overview and architecture

This commit is contained in:
rob
2026-01-05 03:44:38 -04:00
parent 5d2bea0b85
commit 32b77c3378
3873 changed files with 722247 additions and 6 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

@@ -0,0 +1,70 @@
@startuml deployment-architecture
skinparam backgroundColor white
title SmartTools Deployment Architecture
node "Production Server" {
package "Application" {
[Flask App Port 5050] as Flask
[Gunicorn WSGI] as Gunicorn
}
package "Process Management" {
[systemd Service] as Systemd
}
database "SQLite Database" as DB
folder "Static Files" {
[CSS/JS/Images] as Static
}
}
node "Reverse Proxy" {
[nginx] as Nginx
}
cloud "Internet" {
actor "Users" as Users
[Sentry.io] as Sentry
}
node "Development" {
[Flask Dev Server] as DevFlask
database "Dev SQLite" as DevDB
}
Users --> Nginx : HTTPS 443
Nginx --> Gunicorn : HTTP 5050
Gunicorn --> Flask
Flask --> DB
Flask --> Static
Systemd --> Gunicorn : manages
Flask ..> Sentry : error reports
Users --> DevFlask : HTTP 5050
DevFlask --> DevDB
note right of Flask
Environment Variables:
FLASK_ENV=production
SECRET_KEY=<secret>
SENTRY_DSN=<dsn>
SITE_URL=https://...
end note
note right of Systemd
/etc/systemd/system/
smarttools-web.service
gunicorn -w 4
-b 0.0.0.0:5050
end note
note bottom of Nginx
SSL termination
Proxy to localhost:5050
Static file caching
end note
@enduml
+108
View File
@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="1013px" preserveAspectRatio="none" style="width:837px;height:1013px;" version="1.1" viewBox="0 0 837 1013" width="837px" zoomAndPan="magnify"><defs><filter height="300%" id="f14pd9ytz8pwcu" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><text fill="#000000" font-family="sans-serif" font-size="18" lengthAdjust="spacingAndGlyphs" textLength="316" x="264" y="19.2419">SmartTools Deployment Architecture</text><!--MD5=[bf6dbfa945a298ff2f55201b142cffd4]
cluster Production Server--><polygon fill="#FFFFFF" filter="url(#f14pd9ytz8pwcu)" points="307,59.0158,317,49.0158,646,49.0158,646,733.5158,636,743.5158,307,743.5158,307,59.0158" style="stroke: #000000; stroke-width: 1.5;"/><line style="stroke: #000000; stroke-width: 1.5;" x1="636" x2="645" y1="59.0158" y2="50.0158"/><line style="stroke: #000000; stroke-width: 1.5;" x1="307" x2="636" y1="59.0158" y2="59.0158"/><line style="stroke: #000000; stroke-width: 1.5;" x1="636" x2="636" y1="59.0158" y2="743.5158"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="125" x="410" y="76.9818">Production Server</text><!--MD5=[d64883541e3d62e3241c4d12decf100d]
cluster Application--><polygon fill="#FFFFFF" filter="url(#f14pd9ytz8pwcu)" points="384,272.0158,469,272.0158,476,297.0838,568,297.0838,568,525.0158,384,525.0158,384,272.0158" style="stroke: #000000; stroke-width: 1.5;"/><line style="stroke: #000000; stroke-width: 1.5;" x1="384" x2="476" y1="297.0838" y2="297.0838"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="79" x="388" y="288.9818">Application</text><!--MD5=[370ff24fc3a0e8ba62e98200d00118e6]
cluster Process Management--><polygon fill="#FFFFFF" filter="url(#f14pd9ytz8pwcu)" points="440,100.0158,595,100.0158,602,125.0838,605,125.0838,605,193.0158,440,193.0158,440,100.0158" style="stroke: #000000; stroke-width: 1.5;"/><line style="stroke: #000000; stroke-width: 1.5;" x1="440" x2="602" y1="125.0838" y2="125.0838"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="149" x="444" y="116.9818">Process Management</text><!--MD5=[9499d2a3e9c4b5e4d253002427159a31]
cluster Static Files--><polygon fill="#FFFFFF" filter="url(#f14pd9ytz8pwcu)" points="472,626.5158,552,626.5158,559,651.5838,622,651.5838,622,719.5158,472,719.5158,472,626.5158" style="stroke: #000000; stroke-width: 1.5;"/><line style="stroke: #000000; stroke-width: 1.5;" x1="472" x2="559" y1="651.5838" y2="651.5838"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="74" x="476" y="643.4818">Static Files</text><!--MD5=[5295225a1b720472ed2c31e7a5fddf31]
cluster Reverse Proxy--><polygon fill="#FFFFFF" filter="url(#f14pd9ytz8pwcu)" points="96,105.0158,106,95.0158,270,95.0158,270,183.0158,260,193.0158,96,193.0158,96,105.0158" style="stroke: #000000; stroke-width: 1.5;"/><line style="stroke: #000000; stroke-width: 1.5;" x1="260" x2="269" y1="105.0158" y2="96.0158"/><line style="stroke: #000000; stroke-width: 1.5;" x1="96" x2="260" y1="105.0158" y2="105.0158"/><line style="stroke: #000000; stroke-width: 1.5;" x1="260" x2="260" y1="105.0158" y2="193.0158"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="98" x="130" y="122.9818">Reverse Proxy</text><!--MD5=[07074073229a58b98d86dbf75a12e332]
cluster Internet--><path d="M87.3135,613.2923 C90.934,607.6111 95.7313,607.9564 98.9245,613.7256 C101.316,607.5171 107.344,606.6158 111.7531,611.3632 C115.5987,605.7696 122.3717,605.9803 125.7503,611.9295 C129.5564,606.2132 133.9904,604.5846 138.5761,611.113 C142.3901,605.868 148.1654,605.8736 151.524,611.648 C154.7351,605.9172 159.9637,605.1303 164.277,610.3663 C169.0939,605.9217 172.7586,606.0456 176.3175,611.845 C179.6722,604.8896 185.5857,606.3107 189.2754,611.487 C194.1411,605.6237 198.6981,606.4526 202.045,613.0442 C205.1645,607.2217 210.6144,606.6119 214.6698,611.9892 C218.4506,604.3656 225.3111,606.1596 228.8303,612.2591 C236.4093,605.8976 245.8299,608.1431 247,619.0158 C247.1145,619.0481 247.125,619.1422 247.0334,619.2099 C253.675,623.2139 254.6788,629.3503 247.5154,633.8101 C254.2239,636.5719 254.9705,642.4215 248.561,646.2829 C256.2988,649.855 256.1081,657.0164 248.756,660.7915 C256.3786,664.2511 256.2312,670.499 249.715,674.9184 C255.2218,678.5479 255.0536,683.7863 249.7243,687.4237 C255.8167,692.1852 254.6403,696.0799 248.9762,700.1504 C255.7087,704.9882 254.1384,709.3789 248.5155,713.7997 C255.7854,724.885 252.6093,731.3041 240,734.0158 C241.2019,734.3364 241.1335,735.2133 240.453,735.9702 C236.4686,742.3005 230.6654,741.3734 227.2228,735.3881 C224.7413,740.8599 220.1852,741.5486 216.406,736.7557 C211.9778,742.3026 205.2532,742.6547 202.0989,735.3387 C199.0713,741.7502 193.4544,741.6507 190.1534,735.601 C186.5546,742.8685 181.0562,744.0049 176.099,736.9324 C172.1142,742.5425 167.1372,743.2467 163.1626,736.9215 C159.4771,742.39 154.3977,742.417 150.3523,737.3595 C146.5812,743.3469 142.2365,743.1063 138.6152,737.2114 C133.6515,743.3285 129.0262,741.5152 125.6205,735.5374 C120.9373,741.9925 116.4213,741.7024 112.1825,735.0807 C109.5391,740.5288 104.1937,741.0857 100.3146,736.5495 C90.5886,743.6431 79.5228,740.4981 79,727.0158 C80.0728,726.3544 81.1548,726.6475 81.1872,728.0949 C73.8647,724.5613 73.6506,720.6487 79.5551,715.4128 C71.5146,711.8601 70.1541,707.6112 76.4422,700.9703 C69.4227,696.3705 70.4187,691.9729 76.4626,687.3927 C68.6028,683.4849 68.9931,678.7664 74.7494,673.2136 C68.0649,667.6553 70.2739,663.6494 76.3752,659.6202 C70.8848,654.7509 70.8741,649.8484 78.1619,646.7534 C72.0583,642.0273 72.825,637.7459 78.525,633.3393 C69.5212,624.107 74.6807,614.1238 86,612.0158 C86.9422,611.8681 87.5521,612.225 87.3135,613.2923 " fill="#FFFFFF" style="stroke: #000000; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="57" x="134.5" y="631.9818">Internet</text><!--MD5=[88c8cc1bbe8392d0e40b388bb225fee7]
cluster Development--><polygon fill="#FFFFFF" filter="url(#f14pd9ytz8pwcu)" points="22,804.5158,32,794.5158,191,794.5158,191,991.5158,181,1001.5158,22,1001.5158,22,804.5158" style="stroke: #000000; stroke-width: 1.5;"/><line style="stroke: #000000; stroke-width: 1.5;" x1="181" x2="190" y1="804.5158" y2="795.5158"/><line style="stroke: #000000; stroke-width: 1.5;" x1="22" x2="181" y1="804.5158" y2="804.5158"/><line style="stroke: #000000; stroke-width: 1.5;" x1="181" x2="181" y1="804.5158" y2="1001.5158"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="93" x="56" y="822.4818">Development</text><!--MD5=[f9ba5bf973e7832d2ce5ed59077b93f7]
entity DB--><path d="M323,670.0158 C323,660.0158 388,660.0158 388,660.0158 C388,660.0158 453,660.0158 453,670.0158 L453,698.0838 C453,708.0838 388,708.0838 388,708.0838 C388,708.0838 323,708.0838 323,698.0838 L323,670.0158 " fill="#FEFECE" filter="url(#f14pd9ytz8pwcu)" style="stroke: #000000; stroke-width: 1.5;"/><path d="M323,670.0158 C323,680.0158 388,680.0158 388,680.0158 C388,680.0158 453,680.0158 453,670.0158 " fill="none" style="stroke: #000000; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="110" x="333" y="698.9818">SQLite Database</text><!--MD5=[745383965df552ee196eed28c44c08f6]
entity Flask--><rect fill="#FEFECE" filter="url(#f14pd9ytz8pwcu)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="151" x="400.5" y="470.0158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="395.5" y="475.0158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="395.5" y="499.0838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="131" x="410.5" y="494.9818">Flask App Port 5050</text><!--MD5=[973ed0ee46117ce24411a1e9e8e36217]
entity Gunicorn--><rect fill="#FEFECE" filter="url(#f14pd9ytz8pwcu)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="122" x="415" y="310.0158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="410" y="315.0158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="410" y="339.0838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="102" x="425" y="334.9818">Gunicorn WSGI</text><!--MD5=[1bbf2b016c5068e77d803223add63924]
entity Systemd--><rect fill="#FEFECE" filter="url(#f14pd9ytz8pwcu)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="128" x="458" y="138.0158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="453" y="143.0158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="453" y="167.0838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="108" x="468" y="162.9818">systemd Service</text><!--MD5=[169e6db3b10d0b8bb941037d67bc5092]
entity Static--><rect fill="#FEFECE" filter="url(#f14pd9ytz8pwcu)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="488.5" y="664.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="483.5" y="669.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="483.5" y="693.5838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="97" x="498.5" y="689.4818">CSS/JS/Images</text><!--MD5=[b7709df717efd9e6df0e9843c5de81db]
entity Nginx--><rect fill="#FEFECE" filter="url(#f14pd9ytz8pwcu)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="58" x="182" y="138.0158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="177" y="143.0158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="177" y="167.0838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="38" x="192" y="162.9818">nginx</text><!--MD5=[feb5939ce823acf5a8245c3205b3c7a1]
entity Users--><ellipse cx="106" cy="651.5158" fill="#FEFECE" filter="url(#f14pd9ytz8pwcu)" rx="8" ry="8" style="stroke: #A80036; stroke-width: 1.5;"/><path d="M106,659.5158 L106,686.5158 M93,667.5158 L119,667.5158 M106,686.5158 L93,701.5158 M106,686.5158 L119,701.5158 " fill="none" filter="url(#f14pd9ytz8pwcu)" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="38" x="87" y="721.9818">Users</text><!--MD5=[8c1934be4febfaef4a9f4ab6ed25d16c]
entity Sentry--><rect fill="#FEFECE" filter="url(#f14pd9ytz8pwcu)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="79" x="160.5" y="664.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="155.5" y="669.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="155.5" y="693.5838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="59" x="170.5" y="689.4818">Sentry.io</text><!--MD5=[0e0fee03ef7cf82177429b42a2c2c9e0]
entity DevFlask--><rect fill="#FEFECE" filter="url(#f14pd9ytz8pwcu)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="129" x="41.5" y="837.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="36.5" y="842.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="36.5" y="866.5838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="109" x="51.5" y="862.4818">Flask Dev Server</text><!--MD5=[81a082dc0398f0a3f71e99f4328413ba]
entity DevDB--><path d="M60,947.5158 C60,937.5158 106,937.5158 106,937.5158 C106,937.5158 152,937.5158 152,947.5158 L152,975.5838 C152,985.5838 106,985.5838 106,985.5838 C106,985.5838 60,985.5838 60,975.5838 L60,947.5158 " fill="#FEFECE" filter="url(#f14pd9ytz8pwcu)" style="stroke: #000000; stroke-width: 1.5;"/><path d="M60,947.5158 C60,957.5158 106,957.5158 106,957.5158 C106,957.5158 152,957.5158 152,947.5158 " fill="none" style="stroke: #000000; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="72" x="70" y="976.4818">Dev SQLite</text><path d="M662.5,440.0158 L662.5,485.5158 L551.64,489.5158 L662.5,493.5158 L662.5,538.5459 A0,0 0 0 0 662.5,538.5459 L825.5,538.5459 A0,0 0 0 0 825.5,538.5459 L825.5,450.0158 L815.5,440.0158 L662.5,440.0158 A0,0 0 0 0 662.5,440.0158 " fill="#FBFB77" filter="url(#f14pd9ytz8pwcu)" style="stroke: #A80036; stroke-width: 1.0;"/><path d="M815.5,440.0158 L815.5,450.0158 L825.5,450.0158 L815.5,440.0158 " fill="#FBFB77" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="138" x="668.5" y="458.9129">Environment Variables:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="142" x="668.5" y="476.6189">FLASK_ENV=production</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="129" x="668.5" y="494.3249">SECRET_KEY=&lt;secret&gt;</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="121" x="668.5" y="512.0309">SENTRY_DSN=&lt;dsn&gt;</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="117" x="668.5" y="529.7369">SITE_URL=https://...</text><path d="M661.5,108.0158 L661.5,153.5158 L586.16,157.5158 L661.5,161.5158 L661.5,206.5459 A0,0 0 0 0 661.5,206.5459 L820.5,206.5459 A0,0 0 0 0 820.5,206.5459 L820.5,118.0158 L810.5,108.0158 L661.5,108.0158 A0,0 0 0 0 661.5,108.0158 " fill="#FBFB77" filter="url(#f14pd9ytz8pwcu)" style="stroke: #A80036; stroke-width: 1.0;"/><path d="M810.5,108.0158 L810.5,118.0158 L820.5,118.0158 L810.5,108.0158 " fill="#FBFB77" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="132" x="667.5" y="126.9129">/etc/systemd/system/</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="138" x="667.5" y="144.6189">smarttools-web.service</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="0" x="670.5" y="162.3249"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="81" x="667.5" y="180.0309">gunicorn -w 4</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="83" x="667.5" y="197.7369">-b 0.0.0.0:5050</text><path d="M131,298.0158 L131,361.1339 A0,0 0 0 0 131,361.1339 L291,361.1339 A0,0 0 0 0 291,361.1339 L291,308.0158 L281,298.0158 L215,298.0158 L211,177.0358 L207,298.0158 L131,298.0158 A0,0 0 0 0 131,298.0158 " fill="#FBFB77" filter="url(#f14pd9ytz8pwcu)" style="stroke: #A80036; stroke-width: 1.0;"/><path d="M281,298.0158 L281,308.0158 L291,308.0158 L281,298.0158 " fill="#FBFB77" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="95" x="137" y="316.9129">SSL termination</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="139" x="137" y="334.6189">Proxy to localhost:5050</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="102" x="137" y="352.3249">Static file caching</text><!--MD5=[608f8c597b553687ab32249caf8b7553]
link Users to Nginx--><path d="M97.5,641.8558 C83.51,564.5358 62.02,393.6758 114,264.0158 C127.83,229.5258 158.54,199.3658 181.73,180.2258 " fill="none" id="Users-&gt;Nginx" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="185.67,177.0158,176.1626,179.5867,181.7894,180.1688,181.2073,185.7956,185.67,177.0158" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="63" x="85" y="405.9129">HTTPS 443</text><!--MD5=[b609a09f17743f632e2c3f848919ff54]
link Nginx to Gunicorn--><path d="M239.86,177.0358 C289.31,208.7558 389.97,273.3258 442.46,306.9958 " fill="none" id="Nginx-&gt;Gunicorn" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="446.77,309.7658,441.3497,301.5427,442.5599,307.0686,437.0341,308.2788,446.77,309.7658" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="63" x="360" y="251.9129">HTTP 5050</text><!--MD5=[6769a91befd225a488ec640435a7cd47]
link Gunicorn to Flask--><path d="M476,349.2658 C476,378.0058 476,432.9558 476,464.5558 " fill="none" id="Gunicorn-&gt;Flask" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="476,469.7958,480,460.7958,476,464.7958,472,460.7958,476,469.7958" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[9c3821c2b2ea80086ffbae169c67c4f8]
link Flask to DB--><path d="M467.4,509.3258 C451.82,543.4158 418.84,615.5558 400.67,655.3058 " fill="none" id="Flask-&gt;DB" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="398.53,659.9858,405.9204,653.4757,400.6162,655.4419,398.65,650.1377,398.53,659.9858" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[3d4fb86167bfce491c1d476b0a114525]
link Flask to Static--><path d="M483.26,509.1958 C489.4,525.0758 498.39,548.4958 506,569.0158 C517.61,600.3158 530.58,636.6258 538.77,659.6958 " fill="none" id="Flask-&gt;Static" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="540.44,664.4158,541.2026,654.5966,538.7692,659.7033,533.6625,657.2699,540.44,664.4158" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[1978817127f0a473b7921382eca39a84]
link Systemd to Gunicorn--><path d="M516.99,177.0358 C508.58,208.1158 491.63,270.7458 482.38,304.9358 " fill="none" id="Systemd-&gt;Gunicorn" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="481.07,309.7658,487.2933,302.1323,482.3832,304.9414,479.5741,300.0313,481.07,309.7658" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="55" x="501" y="251.9129">manages</text><!--MD5=[3c87541b169c721092784486c2b23b3a]
link Flask to Sentry--><path d="M400.36,495.2258 C345.11,502.7058 272.37,521.6258 228,569.0158 C205.11,593.4558 200.07,633.4858 199.39,659.0258 " fill="none" id="Flask-&gt;Sentry" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="199.31,664.2458,203.4637,655.3157,199.3957,659.2466,195.4649,655.1786,199.31,664.2458" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="77" x="229" y="583.9129">error reports</text><!--MD5=[8a223f306f8ee0d60ce0725198b6b23f]
link Users to DevFlask--><path d="M106,726.4058 C106,759.5858 106,804.9558 106,832.2858 " fill="none" id="Users-&gt;DevFlask" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="106,837.4958,110,828.4958,106,832.4958,102,828.4958,106,837.4958" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="63" x="107" y="774.4129">HTTP 5050</text><!--MD5=[a3ee4cc3f4aa860f6556b91f54aef3b2]
link DevFlask to DevDB--><path d="M106,876.8158 C106,892.2158 106,914.2558 106,931.8458 " fill="none" id="DevFlask-&gt;DevDB" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="106,937.1558,110,928.1558,106,932.1558,102,928.1558,106,937.1558" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[09eedc6ce59bc9693a166faa327a9e06]
@startuml deployment-architecture
skinparam backgroundColor white
title SmartTools Deployment Architecture
node "Production Server" {
package "Application" {
[Flask App Port 5050] as Flask
[Gunicorn WSGI] as Gunicorn
}
package "Process Management" {
[systemd Service] as Systemd
}
database "SQLite Database" as DB
folder "Static Files" {
[CSS/JS/Images] as Static
}
}
node "Reverse Proxy" {
[nginx] as Nginx
}
cloud "Internet" {
actor "Users" as Users
[Sentry.io] as Sentry
}
node "Development" {
[Flask Dev Server] as DevFlask
database "Dev SQLite" as DevDB
}
Users - -> Nginx : HTTPS 443
Nginx - -> Gunicorn : HTTP 5050
Gunicorn - -> Flask
Flask - -> DB
Flask - -> Static
Systemd - -> Gunicorn : manages
Flask ..> Sentry : error reports
Users - -> DevFlask : HTTP 5050
DevFlask - -> DevDB
note right of Flask
Environment Variables:
FLASK_ENV=production
SECRET_KEY=<secret>
SENTRY_DSN=<dsn>
SITE_URL=https://...
end note
note right of Systemd
/etc/systemd/system/
smarttools-web.service
gunicorn -w 4
-b 0.0.0.0:5050
end note
note bottom of Nginx
SSL termination
Proxy to localhost:5050
Static file caching
end note
@enduml
PlantUML version 1.2020.02(Sun Mar 01 06:22:07 AST 2020)
(GPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Java Version: 21.0.8+9-Ubuntu-0ubuntu124.04.1
Operating System: Linux
Default Encoding: UTF-8
Language: en
Country: CA
--></g></svg>

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

+92
View File
@@ -0,0 +1,92 @@
@startuml source-organization
skinparam backgroundColor white
skinparam packageStyle rectangle
title SmartTools Source Code Organization
package "src/smarttools/" {
package "CLI Core" {
[cli.py] as CLI
[tool.py] as Tool
[runner.py] as Runner
[providers.py] as Providers
[config.py] as Config
[manifest.py] as Manifest
}
package "Registry API" {
[registry/app.py] as RegApp
[registry/db.py] as RegDB
[registry/rate_limit.py] as RateLimit
[registry/sync.py] as Sync
[registry/categorize.py] as Cat
[registry/similarity.py] as Sim
}
package "Web UI" {
[web/app.py] as WebApp
[web/routes.py] as Routes
[web/auth.py] as Auth
[web/sessions.py] as Sessions
[web/docs_content.py] as Docs
[web/seo.py] as SEO
[web/filters.py] as Filters
}
package "Client" {
[registry_client.py] as Client
[resolver.py] as Resolver
}
package "TUI" {
[ui.py] as UI
[ui_urwid.py] as Urwid
[ui_snack.py] as Snack
}
}
package "web/templates/" {
[base.html] as Base
[home.html] as Home
[tools/*.html] as ToolsT
[docs/*.html] as DocsT
[auth/*.html] as AuthT
[dashboard/*.html] as DashT
}
package "web/static/" {
[css/output.css] as CSS
[robots.txt] as Robots
}
CLI --> Tool : loads
CLI --> Runner : executes
Runner --> Providers : calls AI
Runner --> Tool : reads steps
WebApp --> Routes : registers
Routes --> RegDB : queries
Routes --> Auth : authenticates
Routes --> Sessions : manages
Client --> RegApp : REST API
RegApp --> RegDB : database ops
RegApp --> RateLimit : enforces
note right of CLI
Entry point for
smarttools command
end note
note right of RegApp
Flask API for
tool registry
end note
note right of WebApp
Flask web app
with Sentry
end note
@enduml
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

+76
View File
@@ -0,0 +1,76 @@
@startuml system-architecture
skinparam backgroundColor white
skinparam componentStyle rectangle
title SmartTools System Architecture
package "User Interfaces" {
[Terminal/CLI] as CLI
[Web Browser] as Browser
}
package "SmartTools CLI" {
[smarttools command] as SMT
[Tool Runner] as Runner
[Registry Client] as RegClient
}
package "Web Application" {
[Flask Web App] as WebApp
[Jinja Templates] as Templates
[Static Assets] as Static
}
package "Registry API" {
[Flask API] as API
[Rate Limiter] as RateLimit
[Auth Handler] as Auth
}
package "Data Layer" {
database "SQLite (FTS5)" as DB
folder "~/.smarttools/" as ToolDir
}
package "External" {
[AI Providers] as Providers
[Sentry] as Sentry
}
CLI --> SMT : runs
SMT --> Runner : executes tools
Runner --> ToolDir : reads configs
Runner --> Providers : calls AI
SMT --> RegClient : install/publish
Browser --> WebApp : HTTP requests
WebApp --> Templates : renders
WebApp --> Static : serves CSS/JS
WebApp --> API : internal calls
RegClient --> API : REST API
API --> RateLimit : enforces limits
API --> Auth : validates tokens
API --> DB : queries
WebApp ..> Sentry : reports errors
API ..> Sentry : reports errors
note right of DB
Tables:
- publishers
- tools
- api_tokens
- download_stats
- pageviews
- tools_fts
end note
note right of ToolDir
~/.smarttools/
- <tool>/config.yaml
- providers.yaml
- config.yaml
end note
@enduml
+126
View File
@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="782px" preserveAspectRatio="none" style="width:1497px;height:782px;" version="1.1" viewBox="0 0 1497 782" width="1497px" zoomAndPan="magnify"><defs><filter height="300%" id="f14o8l4xcccnkh" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><text fill="#000000" font-family="sans-serif" font-size="18" lengthAdjust="spacingAndGlyphs" textLength="274" x="615" y="19.2419">SmartTools System Architecture</text><!--MD5=[d1229199517cdd42d5433fd97e4c984e]
cluster User Interfaces--><polygon fill="#FFFFFF" filter="url(#f14o8l4xcccnkh)" points="446.78,48.5158,557.78,48.5158,564.78,73.5838,729.78,73.5838,729.78,141.5158,446.78,141.5158,446.78,48.5158" style="stroke: #000000; stroke-width: 1.5;"/><line style="stroke: #000000; stroke-width: 1.5;" x1="446.78" x2="564.78" y1="73.5838" y2="73.5838"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="105" x="450.78" y="65.4818">User Interfaces</text><!--MD5=[605f52eee8edfae24bd4998ae0416011]
cluster SmartTools CLI--><polygon fill="#FFFFFF" filter="url(#f14o8l4xcccnkh)" points="689.78,192.5158,800.78,192.5158,807.78,217.5838,976.78,217.5838,976.78,427.5158,689.78,427.5158,689.78,192.5158" style="stroke: #000000; stroke-width: 1.5;"/><line style="stroke: #000000; stroke-width: 1.5;" x1="689.78" x2="807.78" y1="217.5838" y2="217.5838"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="105" x="693.78" y="209.4818">SmartTools CLI</text><!--MD5=[9581351d4a2028020f5e3315621c6150]
cluster Web Application--><polygon fill="#FFFFFF" filter="url(#f14o8l4xcccnkh)" points="112.78,334.5158,232.78,334.5158,239.78,359.5838,407.78,359.5838,407.78,571.5158,112.78,571.5158,112.78,334.5158" style="stroke: #000000; stroke-width: 1.5;"/><line style="stroke: #000000; stroke-width: 1.5;" x1="112.78" x2="239.78" y1="359.5838" y2="359.5838"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="114" x="116.78" y="351.4818">Web Application</text><!--MD5=[ba741963d419160dafadc9ff4e43de37]
cluster Registry API--><polygon fill="#FFFFFF" filter="url(#f14o8l4xcccnkh)" points="561.78,478.5158,652.78,478.5158,659.78,503.5838,855.78,503.5838,855.78,739.0158,561.78,739.0158,561.78,478.5158" style="stroke: #000000; stroke-width: 1.5;"/><line style="stroke: #000000; stroke-width: 1.5;" x1="561.78" x2="659.78" y1="503.5838" y2="503.5838"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="85" x="565.78" y="495.4818">Registry API</text><!--MD5=[023fe2768d341cd525e8503fdfbee177]
cluster Data Layer--><polygon fill="#FFFFFF" filter="url(#f14o8l4xcccnkh)" points="879.78,635.0158,959.78,635.0158,966.78,660.0838,1168.78,660.0838,1168.78,750.0158,879.78,750.0158,879.78,635.0158" style="stroke: #000000; stroke-width: 1.5;"/><line style="stroke: #000000; stroke-width: 1.5;" x1="879.78" x2="966.78" y1="660.0838" y2="660.0838"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="74" x="883.78" y="651.9818">Data Layer</text><!--MD5=[056db7644fd855972490fe1694f3faa1]
cluster External--><polygon fill="#FFFFFF" filter="url(#f14o8l4xcccnkh)" points="264.78,646.0158,327.78,646.0158,334.78,671.0838,496.78,671.0838,496.78,739.0158,264.78,739.0158,264.78,646.0158" style="stroke: #000000; stroke-width: 1.5;"/><line style="stroke: #000000; stroke-width: 1.5;" x1="264.78" x2="334.78" y1="671.0838" y2="671.0838"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="57" x="268.78" y="662.9818">External</text><!--MD5=[df6a39b053164d23cc3ecae9c3484a54]
entity CLI--><rect fill="#FEFECE" filter="url(#f14o8l4xcccnkh)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="106" x="607.78" y="86.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="602.78" y="91.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="602.78" y="115.5838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="86" x="617.78" y="111.4818">Terminal/CLI</text><!--MD5=[e9dede37b31fdda9664526f66258b036]
entity Browser--><rect fill="#FEFECE" filter="url(#f14o8l4xcccnkh)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="109" x="463.28" y="86.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="458.28" y="91.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="458.28" y="115.5838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="89" x="473.28" y="111.4818">Web Browser</text><!--MD5=[5001853de30706f6ebd257c1547af469]
entity SMT--><rect fill="#FEFECE" filter="url(#f14o8l4xcccnkh)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="162" x="705.78" y="230.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="700.78" y="235.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="700.78" y="259.5838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="142" x="715.78" y="255.4818">smarttools command</text><!--MD5=[b44dd552d190738f366cb0d4cbe6023c]
entity Runner--><rect fill="#FEFECE" filter="url(#f14o8l4xcccnkh)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="102" x="705.78" y="372.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="700.78" y="377.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="700.78" y="401.5838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="82" x="715.78" y="397.4818">Tool Runner</text><!--MD5=[a535607fc6e1e098ee79a6f04a363106]
entity RegClient--><rect fill="#FEFECE" filter="url(#f14o8l4xcccnkh)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="118" x="842.78" y="372.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="837.78" y="377.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="837.78" y="401.5838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="98" x="852.78" y="397.4818">Registry Client</text><!--MD5=[ed05b4b62fc63017e649bfedb1046e43]
entity WebApp--><rect fill="#FEFECE" filter="url(#f14o8l4xcccnkh)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="118" x="205.78" y="372.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="200.78" y="377.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="200.78" y="401.5838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="98" x="215.78" y="397.4818">Flask Web App</text><!--MD5=[ca4719ec8dae7adc7b82816388a52075]
entity Templates--><rect fill="#FEFECE" filter="url(#f14o8l4xcccnkh)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="123" x="129.28" y="516.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="124.28" y="521.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="124.28" y="545.5838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="103" x="139.28" y="541.4818">Jinja Templates</text><!--MD5=[169e6db3b10d0b8bb941037d67bc5092]
entity Static--><rect fill="#FEFECE" filter="url(#f14o8l4xcccnkh)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="104" x="287.78" y="516.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="282.78" y="521.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="282.78" y="545.5838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="84" x="297.78" y="541.4818">Static Assets</text><!--MD5=[645d8587175d8f7ad110cd370775931d]
entity API--><rect fill="#FEFECE" filter="url(#f14o8l4xcccnkh)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="79" x="639.28" y="516.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="634.28" y="521.5158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="634.28" y="545.5838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="59" x="649.28" y="541.4818">Flask API</text><!--MD5=[d13fa8fc13b0f8aa3fa6202b21bee041]
entity RateLimit--><rect fill="#FEFECE" filter="url(#f14o8l4xcccnkh)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="101" x="578.28" y="684.0158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="573.28" y="689.0158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="573.28" y="713.0838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="81" x="588.28" y="708.9818">Rate Limiter</text><!--MD5=[39222abbbfd087a4fdab60e627dc976f]
entity Auth--><rect fill="#FEFECE" filter="url(#f14o8l4xcccnkh)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="110" x="714.78" y="684.0158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="709.78" y="689.0158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="709.78" y="713.0838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="90" x="724.78" y="708.9818">Auth Handler</text><!--MD5=[f9ba5bf973e7832d2ce5ed59077b93f7]
entity DB--><path d="M895.78,689.5158 C895.78,679.5158 948.78,679.5158 948.78,679.5158 C948.78,679.5158 1001.78,679.5158 1001.78,689.5158 L1001.78,717.5838 C1001.78,727.5838 948.78,727.5838 948.78,727.5838 C948.78,727.5838 895.78,727.5838 895.78,717.5838 L895.78,689.5158 " fill="#FEFECE" filter="url(#f14o8l4xcccnkh)" style="stroke: #000000; stroke-width: 1.5;"/><path d="M895.78,689.5158 C895.78,699.5158 948.78,699.5158 948.78,699.5158 C948.78,699.5158 1001.78,699.5158 1001.78,689.5158 " fill="none" style="stroke: #000000; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="86" x="905.78" y="718.4818">SQLite (FTS5)</text><!--MD5=[254dae887335c3822701f7dbc12ae4f5]
entity ToolDir--><polygon fill="#FEFECE" filter="url(#f14o8l4xcccnkh)" points="1037.28,673.0158,1083.28,673.0158,1090.28,694.0158,1152.28,694.0158,1152.28,734.1517,1037.28,734.1517,1037.28,673.0158" style="stroke: #000000; stroke-width: 1.5;"/><line style="stroke: #000000; stroke-width: 1.5;" x1="1037.28" x2="1090.28" y1="694.0158" y2="694.0158"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="85" x="1047.28" y="715.9818">/.smarttools/</text><!--MD5=[fa5f2ca45d7d71a02e00e9182531f689]
entity Providers--><rect fill="#FEFECE" filter="url(#f14o8l4xcccnkh)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="101" x="379.28" y="684.0158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="374.28" y="689.0158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="374.28" y="713.0838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="81" x="389.28" y="708.9818">AI Providers</text><!--MD5=[8c1934be4febfaef4a9f4ab6ed25d16c]
entity Sentry--><rect fill="#FEFECE" filter="url(#f14o8l4xcccnkh)" height="39.0679" style="stroke: #A80036; stroke-width: 1.5;" width="63" x="281.28" y="684.0158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="276.28" y="689.0158"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="276.28" y="713.0838"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="43" x="291.28" y="708.9818">Sentry</text><path d="M1187.28,636.5158 L1187.28,770.458 L1310.28,770.458 L1310.28,646.5158 L1300.28,636.5158 L1187.28,636.5158 " fill="#FBFB77" filter="url(#f14o8l4xcccnkh)" style="stroke: #A80036; stroke-width: 1.0;"/><path d="M1300.28,636.5158 L1300.28,646.5158 L1310.28,646.5158 L1300.28,636.5158 " fill="#FBFB77" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="41" x="1193.28" y="655.4129">Tables:</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="69" x="1193.28" y="673.1189">- publishers</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="37" x="1193.28" y="690.8249">- tools</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="72" x="1193.28" y="708.5309">- api_tokens</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="102" x="1193.28" y="726.2369">- download_stats</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="70" x="1193.28" y="743.9429">- pageviews</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="58" x="1193.28" y="761.649">- tools_fts</text><path d="M1345.78,663.0158 L1345.78,743.8399 L1485.78,743.8399 L1485.78,673.0158 L1475.78,663.0158 L1345.78,663.0158 " fill="#FBFB77" filter="url(#f14o8l4xcccnkh)" style="stroke: #A80036; stroke-width: 1.0;"/><path d="M1475.78,663.0158 L1475.78,673.0158 L1485.78,673.0158 L1475.78,663.0158 " fill="#FBFB77" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="78" x="1351.78" y="681.9129">/.smarttools/</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="119" x="1351.78" y="699.6189">- &lt;tool&gt;/config.yaml</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="96" x="1351.78" y="717.3249">- providers.yaml</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="76" x="1351.78" y="735.0309">- config.yaml</text><!--MD5=[a914275849a484c1781b941144137745]
link CLI to SMT--><path d="M677.25,125.5758 C700.27,151.5158 742,198.5558 766.77,226.4758 " fill="none" id="CLI-&gt;SMT" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="770.3,230.4458,767.3282,221.056,766.9853,226.7025,761.3388,226.3596,770.3,230.4458" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="27" x="720.78" y="172.4129">runs</text><!--MD5=[085d57b09593e7e9cf7687a504fdb484]
link SMT to Runner--><path d="M764.15,269.9658 C756.11,278.1358 748.01,288.3658 743.78,299.5158 C735.45,321.4558 740.88,348.1858 747.07,367.0758 " fill="none" id="SMT-&gt;Runner" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="748.82,372.1858,749.6978,362.3762,747.2046,367.454,742.1268,364.9608,748.82,372.1858" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="86" x="744.78" y="314.4129">executes tools</text><!--MD5=[4a6a838f5ff88306718e7b1811ffe0fb]
link Runner to ToolDir--><path d="M782.14,411.7858 C802.47,427.0858 831.51,449.5258 855.78,470.5158 C931.98,536.4258 947.82,556.4958 1019.78,627.0158 C1033.77,640.7258 1049.04,656.0358 1062.12,669.2558 " fill="none" id="Runner-&gt;ToolDir" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="1065.75,672.9358,1062.2702,663.7222,1062.236,669.379,1056.5792,669.3447,1065.75,672.9358" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="79" x="970.78" y="541.4129">reads configs</text><!--MD5=[31706ce452168553514d7b7f71f929ef]
link Runner to Providers--><path d="M705.48,399.0058 C653.73,407.3258 573.96,426.6858 519.78,470.5158 C454.12,523.6258 436.26,630.8858 431.48,678.6458 " fill="none" id="Runner-&gt;Providers" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="430.99,683.8958,435.7951,675.2987,431.4465,678.9167,427.8285,674.5682,430.99,683.8958" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="40" x="520.78" y="541.4129">calls AI</text><!--MD5=[c94d411a20dc9d18f64ad7b39e7d3d31]
link SMT to RegClient--><path d="M807.09,269.6558 C816.22,278.3958 826.91,289.1458 835.78,299.5158 C854.84,321.7958 874.17,349.2958 886.94,368.3058 " fill="none" id="SMT-&gt;RegClient" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="889.73,372.4658,888.0428,362.7626,886.9466,368.3122,881.397,367.216,889.73,372.4658" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="84" x="849.78" y="314.4129">install/publish</text><!--MD5=[03b7a3b7bcc40b0c5fa5584932e46fbc]
link Browser to WebApp--><path d="M501.18,125.6458 C456.41,175.9058 333.31,314.0858 284.8,368.5358 " fill="none" id="Browser-&gt;WebApp" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="281.44,372.3158,290.4176,368.2658,284.7698,368.5859,284.4497,362.9381,281.44,372.3158" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="87" x="449.78" y="255.4129">HTTP requests</text><!--MD5=[a35c05fcd6e2843340593eaa15d9f1c6]
link WebApp to Templates--><path d="M209.54,411.7558 C195.42,419.3358 181.94,429.6558 173.78,443.5158 C161.54,464.3058 169.36,491.9858 178.01,511.4358 " fill="none" id="WebApp-&gt;Templates" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="180.21,516.1858,180.0644,506.3381,178.112,511.6473,172.8028,509.6949,180.21,516.1858" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="46" x="174.78" y="458.4129">renders</text><!--MD5=[21fd5f8483cead33a28cc749629eb61e]
link WebApp to Static--><path d="M259.69,411.5258 C256.7,426.0158 254.73,446.3558 261.78,462.5158 C262.15,463.3658 292.79,491.7158 315.55,512.7058 " fill="none" id="WebApp-&gt;Static" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="319.42,516.2758,315.5155,507.234,315.7443,512.8862,310.0921,513.1151,319.42,516.2758" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="79" x="262.78" y="458.4129">serves CSS/JS</text><!--MD5=[430e7c55d2c04d254fb667505dce0de7]
link WebApp to API--><path d="M324.03,409.7758 C375.37,424.6458 451.48,447.5158 516.78,470.5158 C557.06,484.7058 602.13,502.8358 634.49,516.2658 " fill="none" id="WebApp-&gt;API" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="639.17,518.2058,632.4086,511.0446,634.5567,516.2777,629.3236,518.4258,639.17,518.2058" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="74" x="490.78" y="458.4129">internal calls</text><!--MD5=[ac56397c80aa206a439b95a4bc6e68f5]
link RegClient to API--><path d="M872.62,411.5758 C831.31,437.8858 755.9,485.9058 712.31,513.6658 " fill="none" id="RegClient-&gt;API" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="707.94,516.4458,717.6795,514.9822,712.1565,513.7587,713.3801,508.2357,707.94,516.4458" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="52" x="817.78" y="458.4129">REST API</text><!--MD5=[cb843315cbb96b5d12052ada081a7869]
link API to RateLimit--><path d="M640.62,555.6758 C628.49,563.6758 616.49,574.2758 609.78,587.5158 C594.93,616.7858 606.52,655.1258 617.01,679.2958 " fill="none" id="API-&gt;RateLimit" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="619.09,683.9358,619.0585,674.087,617.0447,679.3733,611.7585,677.3595,619.09,683.9358" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="86" x="610.78" y="602.4129">enforces limits</text><!--MD5=[35b34b4d27a4d3597af9f2ffda93764a]
link API to Auth--><path d="M689.04,555.6758 C705.74,586.0558 738.77,646.1158 756.98,679.2358 " fill="none" id="API-&gt;Auth" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="759.55,683.9158,758.7139,674.1025,757.1388,679.5357,751.7056,677.9605,759.55,683.9158" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="97" x="716.78" y="602.4129">validates tokens</text><!--MD5=[b874c2a0ce20abc088b74c0c7c933d7a]
link API to DB--><path d="M718.54,546.3158 C748.56,554.4158 790.03,567.9458 822.78,587.5158 C863,611.5458 901.64,649.8058 925.28,675.5158 " fill="none" id="API-&gt;DB" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="928.66,679.2158,925.5549,669.8693,925.2924,675.52,919.6416,675.2575,928.66,679.2158" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="44" x="849.78" y="602.4129">queries</text><!--MD5=[5e0692d7836b022fdee864f5eba8d7eb]
link WebApp to Sentry--><path d="M205.65,393.8858 C149.35,398.2458 67.37,414.6058 26.78,470.5158 C-1.68,509.7158 0.14,539.0558 26.78,579.5158 C83.01,664.9158 212.32,691.2658 275.99,699.2058 " fill="none" id="WebApp-&gt;Sentry" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="281.2,699.8358,272.7441,694.7863,276.236,699.2367,271.7856,702.7286,281.2,699.8358" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="83" x="27.78" y="541.4129">reports errors</text><!--MD5=[3c65e303467f427a981473c1f270161b]
link API to Sentry--><path d="M639.09,547.4858 C556.57,569.3658 373.17,618.6058 361.78,627.0158 C343.74,640.3358 330.52,662.4458 322.4,679.3058 " fill="none" id="API-&gt;Sentry" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="320.24,683.9158,327.6796,677.462,322.3607,679.3879,320.4349,674.0689,320.24,683.9158" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="83" x="490.78" y="602.4129">reports errors</text><!--MD5=[ab5d13770903a4b42001319b58679495]
link DB to GMN39--><path d="M961.95,679.1458 C976.66,654.7658 1003,618.7358 1037.28,603.2658 C1083.46,582.4358 1104.6,583.5558 1151.28,603.2658 C1168.83,610.6858 1184.79,622.9758 1198.48,636.4558 " fill="none" id="DB-GMN39" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><!--MD5=[f1d4897222436a13cb915f45ce489a7c]
link ToolDir to GMN42--><path d="M1113.13,672.5458 C1129.27,648.6358 1155.17,617.3258 1187.28,603.2658 C1237.35,581.3358 1259.51,582.9958 1310.28,603.2658 C1340.55,615.3558 1367.1,640.7158 1385.99,662.9058 " fill="none" id="ToolDir-GMN42" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><!--MD5=[27421e287e173a193baeb787c08332a4]
@startuml system-architecture
skinparam backgroundColor white
skinparam componentStyle rectangle
title SmartTools System Architecture
package "User Interfaces" {
[Terminal/CLI] as CLI
[Web Browser] as Browser
}
package "SmartTools CLI" {
[smarttools command] as SMT
[Tool Runner] as Runner
[Registry Client] as RegClient
}
package "Web Application" {
[Flask Web App] as WebApp
[Jinja Templates] as Templates
[Static Assets] as Static
}
package "Registry API" {
[Flask API] as API
[Rate Limiter] as RateLimit
[Auth Handler] as Auth
}
package "Data Layer" {
database "SQLite (FTS5)" as DB
folder "~/.smarttools/" as ToolDir
}
package "External" {
[AI Providers] as Providers
[Sentry] as Sentry
}
CLI - -> SMT : runs
SMT - -> Runner : executes tools
Runner - -> ToolDir : reads configs
Runner - -> Providers : calls AI
SMT - -> RegClient : install/publish
Browser - -> WebApp : HTTP requests
WebApp - -> Templates : renders
WebApp - -> Static : serves CSS/JS
WebApp - -> API : internal calls
RegClient - -> API : REST API
API - -> RateLimit : enforces limits
API - -> Auth : validates tokens
API - -> DB : queries
WebApp ..> Sentry : reports errors
API ..> Sentry : reports errors
note right of DB
Tables:
- publishers
- tools
- api_tokens
- download_stats
- pageviews
- tools_fts
end note
note right of ToolDir
~/.smarttools/
- <tool>/config.yaml
- providers.yaml
- config.yaml
end note
@enduml
PlantUML version 1.2020.02(Sun Mar 01 06:22:07 AST 2020)
(GPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Java Version: 21.0.8+9-Ubuntu-0ubuntu124.04.1
Operating System: Linux
Default Encoding: UTF-8
Language: en
Country: CA
--></g></svg>

After

Width:  |  Height:  |  Size: 26 KiB