diff --git a/data/db.sqlite3 b/data/db.sqlite3
index 6d0342b..6d07b08 100644
Binary files a/data/db.sqlite3 and b/data/db.sqlite3 differ
diff --git a/links/patches.py b/links/patches.py
new file mode 100644
index 0000000..e137f1c
--- /dev/null
+++ b/links/patches.py
@@ -0,0 +1,4 @@
+import sys
+from django import urls
+
+sys.modules['django.core.urlresolvers'] = urls
diff --git a/links/templates/links/help.html b/links/templates/links/help.html
new file mode 100644
index 0000000..d4ac24b
--- /dev/null
+++ b/links/templates/links/help.html
@@ -0,0 +1,141 @@
+{% extends 'base.html' %}
+{% load i18n %}
+{% load markdown_extras %}
+
+{% block extra_css %}
+
+
+
+{% endblock %}
+
+{% block content %}
+
+
+ {% filter markdown %}
+# Frequently Asked Questions (FAQ)
+
+## Link Types
+
+GoLinks supports three types of links: Normal Links, Template Links, and Custom Pages. Each type serves different purposes and has its own use cases.
+
+### 1. Normal Links
+
+Normal links are the simplest type - they provide direct redirection to a target URL.
+
+**Example:**
+- Original URL: `https://github.com/microsoft/vscode`
+- Alias: `vscode`
+- Access: `http://go/vscode` → Redirects to VS Code GitHub repository
+
+**Use Cases:**
+- Quick access to frequently visited websites
+- Shortening long URLs
+- Creating memorable aliases for complex URLs
+
+### 2. Template Links
+
+Template links allow you to create dynamic URLs that can generate the final URL based on parameters. This is particularly useful for search links, API calls, or any URL that needs dynamic parameters.
+
+**Basic Syntax:**
+Use the syntax `{parameter_name, default=default_value}` to define variables in your URL: `https://google.com?q={query, default=hello}`, and set the alias to `google`.
+
+**Examples:**
+- Use the syntax `{query, default=default_value}` to define variables in your URL: `https://google.com?q={query, default=hello}`, and set the alias to `google`.
+
+ Then you can access the link by `http://go/google/`, and the URL will be `https://google.com?q=`.
+
+ When you access the url with `http://go/google/`, the URL will be `https://google.com?q=hello`, which will leverage the `default` value.
+
+### 3. Custom Pages
+
+Custom pages allow you to create markdown-based web pages that can serve as documentation, link collections, or any other content you need.
+
+**Key Features:**
+- Write content in Markdown format
+- Support for headings, lists, code blocks, and links
+- Can include multiple sections and navigation
+- Perfect for creating documentation or resource collections
+
+**Example Use Cases:**
+ Check http://go.junv.cc/custom/today/
+ {% endfilter %}
+
+
+{% endblock %}
diff --git a/links/templates/links/link_detail.html b/links/templates/links/link_detail.html
index 273e2ef..f1eaec3 100644
--- a/links/templates/links/link_detail.html
+++ b/links/templates/links/link_detail.html
@@ -23,7 +23,7 @@
{% endif %}
-
+
diff --git a/links/templates/links/link_list.html b/links/templates/links/link_list.html
index a6bba24..05a4c06 100644
--- a/links/templates/links/link_list.html
+++ b/links/templates/links/link_list.html
@@ -119,7 +119,11 @@
{{ link.click_count }}
diff --git a/links/templatetags/markdown_extras.py b/links/templatetags/markdown_extras.py
new file mode 100644
index 0000000..a4186bf
--- /dev/null
+++ b/links/templatetags/markdown_extras.py
@@ -0,0 +1,8 @@
+from django import template
+import markdown as md
+
+register = template.Library()
+
+@register.filter
+def markdown(value):
+ return md.markdown(value, extensions=['fenced_code', 'tables'])
diff --git a/links/urls.py b/links/urls.py
index 02dd6a7..af8f5fb 100644
--- a/links/urls.py
+++ b/links/urls.py
@@ -8,14 +8,17 @@ urlpatterns = [
path('delete/
/', views.LinkDeleteView.as_view(), name='link_delete'),
path('delete-selected/', views.delete_selected, name='delete_selected'),
path('detail//', views.LinkDetailView.as_view(), name='link_detail'),
- path('search/', views.SearchView.as_view(), name='search'),
+ path('ui/search/', views.SearchView.as_view(), name='search'),
path('link//', views.LinkDetailView.as_view(), name='link_detail'),
path('link//edit/', views.LinkUpdateView.as_view(), name='link_update'),
- path('tools/', views.ToolsView.as_view(), name='tools'),
+ path('ui/tools/', views.ToolsView.as_view(), name='tools'),
path('tools/export-database/', views.export_database, name='export_database'),
+ path('ui/help/', views.HelpView.as_view(), name='help'),
+ path('export/', views.export_links, name='export_links'),
+ # Aliases
path('/', views.redirect_to_original, name='redirect_to_original'),
path('//', views.redirect_to_original, name='redirect_to_original_with_param'),
- path('export/', views.export_links, name='export_links'),
path('alias//', views.LinkDetailView.as_view(), name='link_detail_by_alias'),
+
]
diff --git a/links/views.py b/links/views.py
index bff9b92..f73997b 100644
--- a/links/views.py
+++ b/links/views.py
@@ -429,3 +429,6 @@ def export_database(request):
logger.error(f"Database export failed: {str(e)}")
messages.error(request, _("Failed to export database: {}").format(str(e)))
return redirect('tools')
+
+class HelpView(TemplateView):
+ template_name = 'links/help.html'
diff --git a/new_theme/static/css/dist/styles.css b/new_theme/static/css/dist/styles.css
index 04d2eba..ed31bb6 100644
--- a/new_theme/static/css/dist/styles.css
+++ b/new_theme/static/css/dist/styles.css
@@ -670,6 +670,14 @@ video {
margin-right: auto;
}
+.-ml-px {
+ margin-left: -1px;
+}
+
+.mb-1 {
+ margin-bottom: 0.25rem;
+}
+
.mb-2 {
margin-bottom: 0.5rem;
}
@@ -694,10 +702,6 @@ video {
margin-left: 0.5rem;
}
-.ml-3 {
- margin-left: 0.75rem;
-}
-
.ml-4 {
margin-left: 1rem;
}
@@ -746,14 +750,6 @@ video {
margin-top: 2rem;
}
-.mb-1 {
- margin-bottom: 0.25rem;
-}
-
-.-ml-px {
- margin-left: -1px;
-}
-
.block {
display: block;
}
@@ -782,6 +778,14 @@ video {
display: none;
}
+.h-12 {
+ height: 3rem;
+}
+
+.h-16 {
+ height: 4rem;
+}
+
.h-4 {
height: 1rem;
}
@@ -794,12 +798,12 @@ video {
height: 100vh;
}
-.h-12 {
- height: 3rem;
+.w-12 {
+ width: 3rem;
}
-.h-16 {
- height: 4rem;
+.w-16 {
+ width: 4rem;
}
.w-4 {
@@ -818,22 +822,14 @@ video {
width: 100%;
}
-.w-12 {
- width: 3rem;
-}
-
-.w-16 {
- width: 4rem;
+.min-w-0 {
+ min-width: 0px;
}
.min-w-full {
min-width: 100%;
}
-.min-w-0 {
- min-width: 0px;
-}
-
.max-w-4xl {
max-width: 56rem;
}
@@ -850,6 +846,10 @@ video {
max-width: 32rem;
}
+.max-w-md {
+ max-width: 28rem;
+}
+
.max-w-none {
max-width: none;
}
@@ -862,14 +862,6 @@ video {
max-width: 20rem;
}
-.max-w-3xl {
- max-width: 48rem;
-}
-
-.max-w-md {
- max-width: 28rem;
-}
-
.flex-1 {
flex: 1 1 0%;
}
@@ -920,10 +912,6 @@ video {
align-items: flex-start;
}
-.items-end {
- align-items: flex-end;
-}
-
.items-center {
align-items: center;
}
@@ -953,6 +941,12 @@ video {
row-gap: 2rem;
}
+.space-x-2 > :not([hidden]) ~ :not([hidden]) {
+ --tw-space-x-reverse: 0;
+ margin-right: calc(0.5rem * var(--tw-space-x-reverse));
+ margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
+}
+
.space-x-3 > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 0;
margin-right: calc(0.75rem * var(--tw-space-x-reverse));
@@ -995,12 +989,6 @@ video {
margin-bottom: calc(2rem * var(--tw-space-y-reverse));
}
-.space-x-2 > :not([hidden]) ~ :not([hidden]) {
- --tw-space-x-reverse: 0;
- margin-right: calc(0.5rem * var(--tw-space-x-reverse));
- margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
-}
-
.divide-y > :not([hidden]) ~ :not([hidden]) {
--tw-divide-y-reverse: 0;
border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
@@ -1030,6 +1018,10 @@ video {
white-space: nowrap;
}
+.break-all {
+ word-break: break-all;
+}
+
.rounded {
border-radius: 0.25rem;
}
@@ -1165,11 +1157,21 @@ video {
background-color: rgb(34 197 94 / var(--tw-bg-opacity));
}
+.bg-purple-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(243 232 255 / var(--tw-bg-opacity));
+}
+
.bg-purple-50 {
--tw-bg-opacity: 1;
background-color: rgb(250 245 255 / var(--tw-bg-opacity));
}
+.bg-purple-500 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(168 85 247 / var(--tw-bg-opacity));
+}
+
.bg-red-500 {
--tw-bg-opacity: 1;
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
@@ -1194,30 +1196,14 @@ video {
background-color: rgb(254 249 195 / var(--tw-bg-opacity));
}
-.bg-purple-500 {
- --tw-bg-opacity: 1;
- background-color: rgb(168 85 247 / var(--tw-bg-opacity));
-}
-
-.bg-green-600 {
- --tw-bg-opacity: 1;
- background-color: rgb(22 163 74 / var(--tw-bg-opacity));
-}
-
-.bg-purple-600 {
- --tw-bg-opacity: 1;
- background-color: rgb(147 51 234 / var(--tw-bg-opacity));
-}
-
-.bg-purple-100 {
- --tw-bg-opacity: 1;
- background-color: rgb(243 232 255 / var(--tw-bg-opacity));
-}
-
.p-2 {
padding: 0.5rem;
}
+.p-3 {
+ padding: 0.75rem;
+}
+
.p-4 {
padding: 1rem;
}
@@ -1226,10 +1212,6 @@ video {
padding: 1.5rem;
}
-.p-3 {
- padding: 0.75rem;
-}
-
.px-2 {
padding-left: 0.5rem;
padding-right: 0.5rem;
@@ -1265,6 +1247,11 @@ video {
padding-bottom: 0.25rem;
}
+.py-12 {
+ padding-top: 3rem;
+ padding-bottom: 3rem;
+}
+
.py-2 {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
@@ -1290,23 +1277,10 @@ video {
padding-bottom: 2rem;
}
-.py-12 {
- padding-top: 3rem;
- padding-bottom: 3rem;
-}
-
.pr-3 {
padding-right: 0.75rem;
}
-.pl-5 {
- padding-left: 1.25rem;
-}
-
-.pr-10 {
- padding-right: 2.5rem;
-}
-
.text-left {
text-align: left;
}
@@ -1334,6 +1308,11 @@ video {
line-height: 1;
}
+.text-base {
+ font-size: 1rem;
+ line-height: 1.5rem;
+}
+
.text-lg {
font-size: 1.125rem;
line-height: 1.75rem;
@@ -1354,11 +1333,6 @@ video {
line-height: 1rem;
}
-.text-base {
- font-size: 1rem;
- line-height: 1.5rem;
-}
-
.font-bold {
font-weight: 700;
}
@@ -1411,6 +1385,11 @@ video {
color: rgb(30 64 175 / var(--tw-text-opacity));
}
+.text-gray-300 {
+ --tw-text-opacity: 1;
+ color: rgb(209 213 219 / var(--tw-text-opacity));
+}
+
.text-gray-400 {
--tw-text-opacity: 1;
color: rgb(156 163 175 / var(--tw-text-opacity));
@@ -1466,11 +1445,26 @@ video {
color: rgb(79 70 229 / var(--tw-text-opacity));
}
+.text-purple-500 {
+ --tw-text-opacity: 1;
+ color: rgb(168 85 247 / var(--tw-text-opacity));
+}
+
+.text-purple-600 {
+ --tw-text-opacity: 1;
+ color: rgb(147 51 234 / var(--tw-text-opacity));
+}
+
.text-purple-700 {
--tw-text-opacity: 1;
color: rgb(126 34 206 / var(--tw-text-opacity));
}
+.text-purple-800 {
+ --tw-text-opacity: 1;
+ color: rgb(107 33 168 / var(--tw-text-opacity));
+}
+
.text-red-600 {
--tw-text-opacity: 1;
color: rgb(220 38 38 / var(--tw-text-opacity));
@@ -1491,26 +1485,6 @@ video {
color: rgb(161 98 7 / var(--tw-text-opacity));
}
-.text-gray-300 {
- --tw-text-opacity: 1;
- color: rgb(209 213 219 / var(--tw-text-opacity));
-}
-
-.text-purple-500 {
- --tw-text-opacity: 1;
- color: rgb(168 85 247 / var(--tw-text-opacity));
-}
-
-.text-purple-600 {
- --tw-text-opacity: 1;
- color: rgb(147 51 234 / var(--tw-text-opacity));
-}
-
-.text-purple-800 {
- --tw-text-opacity: 1;
- color: rgb(107 33 168 / var(--tw-text-opacity));
-}
-
.underline {
text-decoration-line: underline;
}
@@ -1519,12 +1493,6 @@ video {
text-decoration-line: line-through;
}
-.shadow {
- --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
- --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
- box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
-}
-
.shadow-md {
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
@@ -1543,20 +1511,6 @@ video {
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
-.ring-1 {
- --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
- --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
- box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
-}
-
-.ring-inset {
- --tw-ring-inset: inset;
-}
-
-.ring-blue-700\/10 {
- --tw-ring-color: rgb(29 78 216 / 0.1);
-}
-
.blur {
--tw-blur: blur(8px);
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
@@ -1678,6 +1632,11 @@ video {
background-color: rgb(21 128 61 / var(--tw-bg-opacity));
}
+.hover\:bg-purple-600:hover {
+ --tw-bg-opacity: 1;
+ background-color: rgb(147 51 234 / var(--tw-bg-opacity));
+}
+
.hover\:bg-red-600:hover {
--tw-bg-opacity: 1;
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
@@ -1688,16 +1647,6 @@ video {
background-color: rgb(185 28 28 / var(--tw-bg-opacity));
}
-.hover\:bg-purple-600:hover {
- --tw-bg-opacity: 1;
- background-color: rgb(147 51 234 / var(--tw-bg-opacity));
-}
-
-.hover\:bg-purple-700:hover {
- --tw-bg-opacity: 1;
- background-color: rgb(126 34 206 / var(--tw-bg-opacity));
-}
-
.hover\:text-blue-700:hover {
--tw-text-opacity: 1;
color: rgb(29 78 216 / var(--tw-text-opacity));
@@ -1782,18 +1731,18 @@ video {
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
}
-.focus\:ring-2:focus {
- --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
- --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
- box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
-}
-
.focus\:ring-1:focus {
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
}
+.focus\:ring-2:focus {
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
+ box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
+}
+
.focus\:ring-blue-200:focus {
--tw-ring-opacity: 1;
--tw-ring-color: rgb(191 219 254 / var(--tw-ring-opacity));
@@ -1819,16 +1768,6 @@ video {
--tw-ring-color: rgb(239 68 68 / var(--tw-ring-opacity));
}
-.focus\:ring-green-500:focus {
- --tw-ring-opacity: 1;
- --tw-ring-color: rgb(34 197 94 / var(--tw-ring-opacity));
-}
-
-.focus\:ring-purple-500:focus {
- --tw-ring-opacity: 1;
- --tw-ring-color: rgb(168 85 247 / var(--tw-ring-opacity));
-}
-
.focus\:ring-opacity-50:focus {
--tw-ring-opacity: 0.5;
}
@@ -1846,10 +1785,6 @@ video {
grid-column: span 2 / span 2;
}
- .sm\:mt-0 {
- margin-top: 0px;
- }
-
.sm\:block {
display: block;
}
@@ -1900,30 +1835,20 @@ video {
margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
}
- .sm\:space-x-4 > :not([hidden]) ~ :not([hidden]) {
- --tw-space-x-reverse: 0;
- margin-right: calc(1rem * var(--tw-space-x-reverse));
- margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));
- }
-
.sm\:space-y-0 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0;
margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(0px * var(--tw-space-y-reverse));
}
- .sm\:rounded-md {
- border-radius: 0.375rem;
+ .sm\:p-12 {
+ padding: 3rem;
}
.sm\:p-6 {
padding: 1.5rem;
}
- .sm\:p-12 {
- padding: 3rem;
- }
-
.sm\:px-6 {
padding-left: 1.5rem;
padding-right: 1.5rem;
@@ -1943,11 +1868,6 @@ video {
font-size: 1rem;
line-height: 1.5rem;
}
-
- .sm\:text-sm {
- font-size: 0.875rem;
- line-height: 1.25rem;
- }
}
@media (min-width: 768px) {
@@ -1965,10 +1885,6 @@ video {
grid-template-columns: repeat(5, minmax(0, 1fr));
}
- .lg\:p-8 {
- padding: 2rem;
- }
-
.lg\:px-8 {
padding-left: 2rem;
padding-right: 2rem;
diff --git a/templates/base.html b/templates/base.html
index a8242c3..6571431 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -70,6 +70,7 @@
diff --git a/url_manager/settings.py b/url_manager/settings.py
index 5fa1db0..a47df01 100644
--- a/url_manager/settings.py
+++ b/url_manager/settings.py
@@ -1,6 +1,7 @@
import os
from pathlib import Path
from django.utils.translation import gettext_lazy as _
+import links.patches # 添加这一行在文件最上方
# 构建路径,如 BASE_DIR / 'subdir'
BASE_DIR = Path(__file__).resolve().parent.parent
@@ -17,6 +18,7 @@ INSTALLED_APPS = [
'tailwind',
'new_theme',
'simplemde',
+ 'markdown', # 只需要基本的markdown包
]
ROOT_URLCONF = 'url_manager.urls'