Skip to content

Commit 95cf94e

Browse files
feat(contrib,admin,auth): move admin, users, groups, accounts to contrib with Django-like structure and update templates/static
- Move admin, users, groups, accounts to src/cotlette/contrib/ - Add Django-like static and template namespacing for admin - Update all template paths and static urls for admin namespace - Refactor all auth/account urls in templates to /auth/... (no /api) - Remove legacy example apps and async example - Update settings.py to use contrib apps - Add setup_admin for admin UI and static mounting - Fix circular imports and router structure for FastAPI
1 parent 8777da5 commit 95cf94e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+4225
-9
lines changed

example/config/settings.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,9 @@
2424
DEBUG = True
2525

2626
INSTALLED_APPS = [
27-
# 'cotlette.apps.admin',
28-
# 'cotlette.apps.users',
27+
'cotlette.contrib.admin',
28+
'cotlette.contrib.auth',
2929
'apps.home',
30-
'apps.admin',
31-
'apps.users',
32-
'apps.accounts',
33-
'apps.groups',
3430
]
3531

3632
TEMPLATES = [

example/manage.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
"""Cotlette's command-line utility for administrative tasks."""
33
import os
44
import sys
5-
import apps.users.models
65

76

87
def main():

example/templates/accounts/login.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ <h3 class="font-weight-bolder text-info text-gradient">Welcome back</h3>
5757
document.addEventListener('DOMContentLoaded', async function () {
5858
try {
5959
// Отправляем POST-запрос на разлогирование
60-
const response = await fetch('/api/users/logout/', {
60+
const response = await fetch('/users/logout/', {
6161
method: 'POST',
6262
headers: {
6363
'Content-Type': 'application/json',
@@ -95,7 +95,7 @@ <h3 class="font-weight-bolder text-info text-gradient">Welcome back</h3>
9595
formData.append('email', email);
9696
formData.append('password', password);
9797

98-
const response = await fetch('/api/users/login/', {
98+
const response = await fetch('/users/login/', {
9999
method: 'POST',
100100
body: formData, // Отправляем данные в формате FormData
101101
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from fastapi import APIRouter, FastAPI
2+
3+
from .urls import router as urls_router
4+
5+
from cotlette.middlewares import PermissionMiddleware
6+
7+
8+
router = APIRouter()
9+
# router.include_router(urls_router, prefix="/admin", include_in_schema=False)
10+
router.include_router(urls_router, prefix="/admin", tags=["restricted"])
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from cotlette.contrib import admin
2+
3+
# Register your models here.

src/cotlette/contrib/admin/apps.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from cotlette.apps.config import AppConfig
2+
3+
4+
class AdminConfig(AppConfig):
5+
name = 'cotlette.contrib.admin'
6+
verbose_name = 'Admin Interface'
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Create your controlles here.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# from cotlette.db import models
2+
3+
# Create your models here.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/* Custom lightweight tweaks for minimalist dashboard */
2+
3+
:root {
4+
--cot-primary: #cb0c9f; /* already used in Soft-UI */
5+
--cot-dark: #344767;
6+
}
7+
8+
/* Hero section */
9+
.hero {
10+
padding: 4rem 1rem;
11+
}
12+
.hero .display-4 {
13+
color: var(--cot-dark);
14+
letter-spacing: -0.5px;
15+
}
16+
.hero p.lead {
17+
color: #6c757d;
18+
}
19+
20+
/* Statistic cards */
21+
.card.shadow-sm {
22+
transition: transform 0.2s ease, box-shadow 0.2s ease;
23+
}
24+
.card.shadow-sm:hover {
25+
transform: translateY(-4px);
26+
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
27+
}
28+
29+
/* Button */
30+
.btn-primary {
31+
background-color: var(--cot-primary);
32+
border-color: var(--cot-primary);
33+
}
34+
.btn-primary:hover {
35+
background-color: #b30c8d;
36+
border-color: #b30c8d;
37+
}
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
<!doctype html>
2+
<html lang="en-us">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>"The install worked successfully! Congratulations!"</title>
6+
<meta name="viewport" content="width=device-width, initial-scale=1">
7+
<style>
8+
html {
9+
line-height: 1.15;
10+
}
11+
a {
12+
color: #092e20;
13+
}
14+
body {
15+
max-width: 960px;
16+
color: #525252;
17+
font-family: "Segoe UI", system-ui, sans-serif;
18+
margin: 0 auto;
19+
}
20+
main {
21+
text-align: center;
22+
}
23+
h1, h2, h3, h4, h5, p, ul {
24+
padding: 0;
25+
margin: 0;
26+
font-weight: 400;
27+
}
28+
.logo {
29+
color: #092e20;
30+
background-position-x: center;
31+
background-repeat: no-repeat;
32+
font-size: 2rem;
33+
font-weight: 700;
34+
margin-top: 16px;
35+
overflow: hidden;
36+
text-decoration: none;
37+
text-indent: 100%;
38+
display: inline-block;
39+
}
40+
.figure {
41+
margin-top: 22vh;
42+
max-width: 265px;
43+
position: relative;
44+
z-index: -9;
45+
overflow: visible;
46+
}
47+
.exhaust__line {
48+
animation: thrust 70ms 100 ease-in-out alternate;
49+
}
50+
.smoke {
51+
animation: smoke .1s 70 ease-in-out alternate;
52+
}
53+
@keyframes smoke {
54+
0% {
55+
transform: translate3d(-5px, 0, 0);
56+
}
57+
100% {
58+
transform: translate3d(5px, 0, 0);
59+
}
60+
}
61+
.flame {
62+
animation: burnInner2 .1s 70 ease-in-out alternate;
63+
}
64+
@keyframes burnInner2 {
65+
0% {
66+
transform: translate3d(0, 0, 0);
67+
}
68+
100% {
69+
transform: translate3d(0, 3px, 0);
70+
}
71+
}
72+
@keyframes thrust {
73+
0% {
74+
opacity: 1;
75+
}
76+
100% {
77+
opacity: .5;
78+
}
79+
}
80+
@media (prefers-reduced-motion: reduce) {
81+
.exhaust__line,
82+
.smoke,
83+
.flame {
84+
animation: none;
85+
}
86+
}
87+
h1 {
88+
font-size: 1.375rem;
89+
max-width: 32rem;
90+
margin: 5px auto 0;
91+
}
92+
main p {
93+
line-height: 1.25;
94+
max-width: 26rem;
95+
margin: 15px auto 0;
96+
}
97+
footer {
98+
display: grid;
99+
grid-template-columns: 1fr 1fr 1fr;
100+
gap: 5px;
101+
padding: 25px 0;
102+
position: fixed;
103+
box-sizing: border-box;
104+
left: 50%;
105+
bottom: 0;
106+
width: 960px;
107+
transform: translateX(-50%);
108+
transform-style: preserve-3d;
109+
border-top: 1px solid #efefef;
110+
}
111+
.option {
112+
display: grid;
113+
grid-template-columns: min-content 1fr;
114+
gap: 10px;
115+
box-sizing: border-box;
116+
text-decoration: none;
117+
}
118+
.option svg {
119+
width: 1.5rem;
120+
height: 1.5rem;
121+
fill: gray;
122+
border: 1px solid #d6d6d6;
123+
padding: 5px;
124+
border-radius: 100%;
125+
}
126+
.option p {
127+
font-weight: 300;
128+
line-height: 1.25;
129+
color: #525252;
130+
display: table;
131+
}
132+
.option .option__heading {
133+
color: #092e20;
134+
font-size: 1.25rem;
135+
font-weight: 400;
136+
}
137+
@media (max-width: 996px) {
138+
body, footer {
139+
max-width: 780px;
140+
}
141+
}
142+
@media (max-width: 800px) {
143+
footer {
144+
height: 100%;
145+
grid-template-columns: 1fr;
146+
gap: 60px;
147+
position: relative;
148+
padding: 25px;
149+
}
150+
.figure {
151+
margin-top: 10px;
152+
}
153+
main {
154+
padding: 0 25px;
155+
}
156+
main h1 {
157+
font-size: 1.25rem;
158+
}
159+
footer {
160+
width: 100%;
161+
margin-top: 50px;
162+
}
163+
}
164+
@media (min-width: 801px) and (max-height: 730px) {
165+
.figure {
166+
margin-top: 80px;
167+
}
168+
}
169+
@media (min-width: 801px) and (max-height: 600px) {
170+
footer {
171+
position: relative;
172+
margin: 135px auto 0;
173+
}
174+
.figure {
175+
margin-top: 50px;
176+
}
177+
}
178+
.sr-only {
179+
clip: rect(1px, 1px, 1px, 1px);
180+
clip-path: inset(50%);
181+
height: 1px;
182+
overflow: hidden;
183+
position: absolute;
184+
white-space: nowrap;
185+
width: 1px;
186+
}
187+
</style>
188+
</head>
189+
<body>
190+
<main>
191+
192+
<img class="figure"
193+
src="/admin/static/401.jpg"
194+
alt="SVG Image"
195+
width="300"
196+
height="300"
197+
>
198+
199+
<h1>Error 401: Unauthorized. But you’re still awesome!</h1>
200+
201+
<p>
202+
<a href="/accounts/login" rel="noopener">Go to the login page</a>
203+
</p>
204+
</main>
205+
</body>
206+
</html>

0 commit comments

Comments
 (0)