Skip to content

Commit c319efa

Browse files
Merge pull request #23 from PKA-OpenDynamics/develop
feat: Add Data License page (CC BY 4.0) and update Swagger documentation
2 parents 0cba08a + 14f9c69 commit c319efa

File tree

3 files changed

+349
-1
lines changed

3 files changed

+349
-1
lines changed

backend/app/main.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,24 @@ async def lifespan(app: FastAPI):
2727

2828
app = FastAPI(
2929
title=settings.PROJECT_NAME,
30-
description="CityLens Smart City Platform - REST API for urban data management with FiWARE NGSI-LD",
30+
description="""CityLens Smart City Platform - REST API for urban data management with FiWARE NGSI-LD
31+
32+
## Data License
33+
34+
**Public Data License:** All public data returned by this API is licensed under
35+
[Creative Commons Attribution 4.0 International (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/)
36+
37+
You are free to:
38+
- **Share** — copy and redistribute the data in any medium or format
39+
- **Adapt** — remix, transform, and build upon the data for any purpose, including commercially
40+
41+
Under the following terms:
42+
- **Attribution** — You must give appropriate credit to CityLens
43+
44+
## API License
45+
46+
The API source code is licensed under GNU General Public License v3.0 (GPL-3.0)
47+
""",
3148
version=settings.VERSION,
3249
openapi_url=f"{settings.API_V1_STR}/openapi.json",
3350
docs_url="/docs",
Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
// Copyright (c) 2025 CityLens Contributors
2+
// Licensed under the GNU General Public License v3.0 (GPL-3.0)
3+
4+
'use client';
5+
6+
import {
7+
Scale,
8+
ExternalLink,
9+
Database,
10+
Globe,
11+
CheckCircle2,
12+
Copy,
13+
FileText,
14+
Users,
15+
Share2,
16+
Briefcase,
17+
Info,
18+
Shield,
19+
BookOpen
20+
} from 'lucide-react';
21+
import { useState } from 'react';
22+
23+
export default function DataLicensePage() {
24+
const [copied, setCopied] = useState(false);
25+
26+
const copyAttribution = () => {
27+
const attribution = `Data from CityLens Smart City Platform (https://citylens.vn) is licensed under CC BY 4.0`;
28+
navigator.clipboard.writeText(attribution);
29+
setCopied(true);
30+
setTimeout(() => setCopied(false), 2000);
31+
};
32+
33+
const licenses = [
34+
{
35+
name: 'Dữ liệu công khai (Public Data)',
36+
license: 'Creative Commons Attribution 4.0 International (CC BY 4.0)',
37+
icon: Database,
38+
color: 'bg-green-500',
39+
description: 'Tất cả dữ liệu địa lý, thống kê và báo cáo công khai',
40+
permissions: ['Chia sẻ', 'Sử dụng thương mại', 'Sửa đổi', 'Phân phối'],
41+
conditions: ['Ghi nhận nguồn (Attribution)'],
42+
link: 'https://creativecommons.org/licenses/by/4.0/',
43+
},
44+
{
45+
name: 'Mã nguồn phần mềm (Source Code)',
46+
license: 'GNU General Public License v3.0 (GPL-3.0)',
47+
icon: FileText,
48+
color: 'bg-blue-500',
49+
description: 'Toàn bộ mã nguồn của nền tảng CityLens',
50+
permissions: ['Sử dụng', 'Sửa đổi', 'Phân phối', 'Thương mại hóa'],
51+
conditions: ['Công khai mã nguồn', 'Giữ nguyên giấy phép', 'Ghi nhận nguồn'],
52+
link: 'https://www.gnu.org/licenses/gpl-3.0.html',
53+
},
54+
{
55+
name: 'API Documentation',
56+
license: 'Creative Commons Attribution 4.0 International (CC BY 4.0)',
57+
icon: BookOpen,
58+
color: 'bg-purple-500',
59+
description: 'Tài liệu API và hướng dẫn sử dụng',
60+
permissions: ['Chia sẻ', 'Sử dụng thương mại', 'Sửa đổi'],
61+
conditions: ['Ghi nhận nguồn (Attribution)'],
62+
link: 'https://creativecommons.org/licenses/by/4.0/',
63+
},
64+
];
65+
66+
const ccByFeatures = [
67+
{
68+
icon: Share2,
69+
title: 'Chia sẻ tự do',
70+
description: 'Sao chép và phân phối lại dữ liệu dưới bất kỳ phương tiện hay định dạng nào',
71+
},
72+
{
73+
icon: Briefcase,
74+
title: 'Sử dụng thương mại',
75+
description: 'Được phép sử dụng dữ liệu cho mục đích thương mại',
76+
},
77+
{
78+
icon: Users,
79+
title: 'Sửa đổi và tái tạo',
80+
description: 'Remix, chuyển đổi và xây dựng dựa trên dữ liệu cho bất kỳ mục đích nào',
81+
},
82+
{
83+
icon: Info,
84+
title: 'Ghi nhận nguồn',
85+
description: 'Phải ghi nhận nguồn gốc dữ liệu từ CityLens một cách phù hợp',
86+
},
87+
];
88+
89+
return (
90+
<div className="min-h-screen bg-gradient-to-br from-white via-green-50/30 to-white dark:from-gray-950 dark:via-green-950/10 dark:to-gray-950">
91+
{/* Hero Section */}
92+
<div className="relative overflow-hidden">
93+
<div className="absolute inset-0 bg-gradient-to-r from-green-600/10 via-transparent to-green-500/10" />
94+
<div className="absolute top-0 left-1/4 w-96 h-96 bg-green-500/20 rounded-full blur-3xl" />
95+
<div className="absolute bottom-0 right-1/4 w-96 h-96 bg-green-600/10 rounded-full blur-3xl" />
96+
97+
<div className="relative max-w-7xl mx-auto px-6 py-16">
98+
<div className="text-center">
99+
{/* Badge */}
100+
<div className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-green-100 dark:bg-green-900/30 border border-green-200 dark:border-green-800 mb-6">
101+
<Scale className="h-4 w-4 text-green-600" />
102+
<span className="text-sm font-medium text-green-700 dark:text-green-400">Open Data License</span>
103+
</div>
104+
105+
{/* Title */}
106+
<h1 className="text-4xl md:text-5xl font-bold text-gray-900 dark:text-white mb-4">
107+
Giấy phép Dữ liệu Mở
108+
</h1>
109+
<p className="text-xl text-gray-600 dark:text-gray-300 max-w-3xl mx-auto mb-8">
110+
CityLens cam kết cung cấp dữ liệu mở cho cộng đồng. Tất cả dữ liệu công khai được xuất bản
111+
với giấy phép <strong className="text-green-600">Creative Commons Attribution 4.0 (CC BY 4.0)</strong>
112+
</p>
113+
114+
{/* CC BY 4.0 Badge */}
115+
<div className="inline-flex items-center gap-4 px-6 py-4 rounded-2xl bg-white dark:bg-gray-800 shadow-xl border border-green-100 dark:border-green-900">
116+
<div className="flex items-center gap-2">
117+
<img
118+
src="https://licensebuttons.net/l/by/4.0/88x31.png"
119+
alt="CC BY 4.0"
120+
className="h-8"
121+
/>
122+
</div>
123+
<div className="text-left">
124+
<p className="font-semibold text-gray-900 dark:text-white">CC BY 4.0</p>
125+
<p className="text-sm text-gray-500 dark:text-gray-400">International License</p>
126+
</div>
127+
<a
128+
href="https://creativecommons.org/licenses/by/4.0/"
129+
target="_blank"
130+
rel="noopener noreferrer"
131+
className="ml-2 p-2 rounded-lg bg-green-100 dark:bg-green-900/50 hover:bg-green-200 dark:hover:bg-green-900 transition-colors"
132+
>
133+
<ExternalLink className="h-5 w-5 text-green-600" />
134+
</a>
135+
</div>
136+
</div>
137+
</div>
138+
</div>
139+
140+
{/* Main Content */}
141+
<div className="max-w-7xl mx-auto px-6 py-12">
142+
{/* CC BY 4.0 Features */}
143+
<section className="mb-16">
144+
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-8 flex items-center gap-3">
145+
<Shield className="h-7 w-7 text-green-600" />
146+
Quyền lợi theo CC BY 4.0
147+
</h2>
148+
149+
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-6">
150+
{ccByFeatures.map((feature, index) => (
151+
<div
152+
key={index}
153+
className="relative p-6 rounded-2xl bg-white dark:bg-gray-800/50 border border-gray-100 dark:border-gray-700 hover:border-green-300 dark:hover:border-green-700 transition-all hover:shadow-lg group"
154+
>
155+
<div className="w-12 h-12 rounded-xl bg-green-100 dark:bg-green-900/30 flex items-center justify-center mb-4 group-hover:scale-110 transition-transform">
156+
<feature.icon className="h-6 w-6 text-green-600" />
157+
</div>
158+
<h3 className="font-semibold text-gray-900 dark:text-white mb-2">{feature.title}</h3>
159+
<p className="text-sm text-gray-600 dark:text-gray-400">{feature.description}</p>
160+
</div>
161+
))}
162+
</div>
163+
</section>
164+
165+
{/* Attribution Box */}
166+
<section className="mb-16">
167+
<div className="relative p-8 rounded-3xl bg-gradient-to-r from-green-600 to-green-500 text-white overflow-hidden">
168+
<div className="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA2MCA2MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxnIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iMC4xIj48Y2lyY2xlIGN4PSIzMCIgY3k9IjMwIiByPSIyIi8+PC9nPjwvZz48L3N2Zz4=')] opacity-30" />
169+
170+
<div className="relative">
171+
<h3 className="text-xl font-bold mb-4 flex items-center gap-2">
172+
<Copy className="h-5 w-5" />
173+
Mẫu ghi nhận nguồn (Attribution)
174+
</h3>
175+
176+
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-4 mb-4">
177+
<code className="text-sm text-white/90 break-all">
178+
Data from CityLens Smart City Platform (https://citylens.vn) is licensed under CC BY 4.0
179+
</code>
180+
</div>
181+
182+
<button
183+
onClick={copyAttribution}
184+
className="inline-flex items-center gap-2 px-6 py-3 rounded-xl bg-white text-green-600 font-semibold hover:bg-green-50 transition-colors"
185+
>
186+
{copied ? (
187+
<>
188+
<CheckCircle2 className="h-5 w-5" />
189+
Đã sao chép!
190+
</>
191+
) : (
192+
<>
193+
<Copy className="h-5 w-5" />
194+
Sao chép Attribution
195+
</>
196+
)}
197+
</button>
198+
</div>
199+
</div>
200+
</section>
201+
202+
{/* All Licenses */}
203+
<section className="mb-16">
204+
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-8 flex items-center gap-3">
205+
<FileText className="h-7 w-7 text-green-600" />
206+
Tất cả Giấy phép
207+
</h2>
208+
209+
<div className="grid md:grid-cols-3 gap-6">
210+
{licenses.map((license, index) => (
211+
<div
212+
key={index}
213+
className="relative p-6 rounded-2xl bg-white dark:bg-gray-800/50 border border-gray-100 dark:border-gray-700 hover:shadow-xl transition-all group"
214+
>
215+
{/* Header */}
216+
<div className="flex items-start gap-4 mb-4">
217+
<div className={`w-12 h-12 rounded-xl ${license.color} flex items-center justify-center flex-shrink-0`}>
218+
<license.icon className="h-6 w-6 text-white" />
219+
</div>
220+
<div className="flex-1 min-w-0">
221+
<h3 className="font-semibold text-gray-900 dark:text-white mb-1">{license.name}</h3>
222+
<p className="text-xs text-green-600 dark:text-green-400 font-medium">{license.license}</p>
223+
</div>
224+
</div>
225+
226+
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">{license.description}</p>
227+
228+
{/* Permissions */}
229+
<div className="mb-4">
230+
<p className="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase mb-2">Được phép</p>
231+
<div className="flex flex-wrap gap-1">
232+
{license.permissions.map((perm, i) => (
233+
<span key={i} className="inline-flex items-center gap-1 px-2 py-1 rounded-md bg-green-100 dark:bg-green-900/30 text-xs text-green-700 dark:text-green-400">
234+
<CheckCircle2 className="h-3 w-3" />
235+
{perm}
236+
</span>
237+
))}
238+
</div>
239+
</div>
240+
241+
{/* Conditions */}
242+
<div className="mb-4">
243+
<p className="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase mb-2">Điều kiện</p>
244+
<div className="flex flex-wrap gap-1">
245+
{license.conditions.map((cond, i) => (
246+
<span key={i} className="inline-flex items-center gap-1 px-2 py-1 rounded-md bg-amber-100 dark:bg-amber-900/30 text-xs text-amber-700 dark:text-amber-400">
247+
<Info className="h-3 w-3" />
248+
{cond}
249+
</span>
250+
))}
251+
</div>
252+
</div>
253+
254+
{/* Link */}
255+
<a
256+
href={license.link}
257+
target="_blank"
258+
rel="noopener noreferrer"
259+
className="inline-flex items-center gap-2 text-sm text-green-600 hover:text-green-700 font-medium group-hover:underline"
260+
>
261+
Xem chi tiết giấy phép
262+
<ExternalLink className="h-4 w-4" />
263+
</a>
264+
</div>
265+
))}
266+
</div>
267+
</section>
268+
269+
{/* API License Info */}
270+
<section className="mb-16">
271+
<div className="p-8 rounded-3xl bg-gray-50 dark:bg-gray-800/30 border border-gray-100 dark:border-gray-700">
272+
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-6 flex items-center gap-3">
273+
<Globe className="h-7 w-7 text-green-600" />
274+
Giấy phép API & OpenAPI Specification
275+
</h2>
276+
277+
<div className="grid md:grid-cols-2 gap-8">
278+
<div>
279+
<h3 className="font-semibold text-gray-900 dark:text-white mb-3">REST API</h3>
280+
<div className="space-y-2 text-sm text-gray-600 dark:text-gray-400">
281+
<p><strong>Version:</strong> 0.3.0</p>
282+
<p><strong>Specification:</strong> OpenAPI 3.1 (OAS 3.1)</p>
283+
<p><strong>Endpoint:</strong> /api/v1/openapi.json</p>
284+
<p><strong>License:</strong> GNU General Public License v3.0</p>
285+
</div>
286+
287+
{/* <a
288+
href="/api/v1/docs"
289+
className="inline-flex items-center gap-2 mt-4 px-4 py-2 rounded-lg bg-green-600 text-white hover:bg-green-700 transition-colors text-sm font-medium"
290+
>
291+
Xem API Documentation
292+
<ExternalLink className="h-4 w-4" />
293+
</a> */}
294+
</div>
295+
296+
<div>
297+
<h3 className="font-semibold text-gray-900 dark:text-white mb-3">Dữ liệu trả về từ API</h3>
298+
<div className="space-y-2 text-sm text-gray-600 dark:text-gray-400">
299+
<p>Tất cả dữ liệu trả về từ các endpoint công khai đều được cấp phép theo <strong className="text-green-600">CC BY 4.0</strong>.</p>
300+
<p>Bạn có thể tự do sử dụng dữ liệu cho:</p>
301+
<ul className="list-disc list-inside ml-2 space-y-1">
302+
<li>Nghiên cứu học thuật</li>
303+
<li>Phát triển ứng dụng</li>
304+
<li>Phân tích dữ liệu</li>
305+
<li>Mục đích thương mại</li>
306+
</ul>
307+
</div>
308+
</div>
309+
</div>
310+
</div>
311+
</section>
312+
313+
{/* Contact */}
314+
<section className="text-center">
315+
<p className="text-gray-600 dark:text-gray-400 mb-4">
316+
Có câu hỏi về giấy phép? Liên hệ với chúng tôi
317+
</p>
318+
<a
319+
href="mailto:[email protected]"
320+
className="inline-flex items-center gap-2 text-green-600 hover:text-green-700 font-medium"
321+
>
322+
323+
<ExternalLink className="h-4 w-4" />
324+
</a>
325+
</section>
326+
</div>
327+
</div>
328+
);
329+
}

web-dashboard/src/components/layout/sidebar.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
AlertTriangle,
1818
BarChart3,
1919
Network,
20+
Scale,
2021
} from 'lucide-react';
2122
import { cn } from '@/lib/utils';
2223
import { useAuth } from '@/components/providers/auth-provider';
@@ -44,6 +45,7 @@ const navigationGroups = [
4445
title: 'System',
4546
items: [
4647
{ name: 'User Management', nameVi: 'Quản lý người dùng', href: '/admin/user-management', icon: Users },
48+
{ name: 'Data License', nameVi: 'Giấy phép dữ liệu', href: '/data-license', icon: Scale },
4749
{ name: 'Settings', nameVi: 'Cài đặt', href: '/settings', icon: Settings },
4850
],
4951
},

0 commit comments

Comments
 (0)