Merge pull request #16 from fossyy/staging

Staging
This commit is contained in:
2024-05-02 23:22:49 +07:00
committed by GitHub
11 changed files with 246 additions and 133 deletions

View File

@ -7,9 +7,11 @@ COPY /view /src/view
RUN npm install -g tailwindcss RUN npm install -g tailwindcss
RUN npm install -g javascript-obfuscator RUN npm install -g javascript-obfuscator
RUN npx tailwindcss -i ./public/input.css -o ./public/output.css RUN npm install -g clean-css-cli
RUN npx tailwindcss -i ./public/input.css -o ./tmp/output.css
RUN javascript-obfuscator ./public/upload.js --compact true --self-defending true --output ./public/upload_obfuscated.js RUN javascript-obfuscator ./public/upload.js --compact true --self-defending true --output ./public/upload_obfuscated.js
RUN javascript-obfuscator ./public/validatePassword.js --compact true --self-defending true --output ./public/validatePassword_obfuscated.js RUN javascript-obfuscator ./public/validatePassword.js --compact true --self-defending true --output ./public/validatePassword_obfuscated.js
RUN cleancss -o ./public/output.css ./tmp/output.css
FROM golang:1.22.2-alpine3.19 AS go_builder FROM golang:1.22.2-alpine3.19 AS go_builder
@ -19,7 +21,7 @@ COPY --from=node_builder /src/public /src/public
COPY --from=node_builder /src/public/upload_obfuscated.js /src/public/upload.js COPY --from=node_builder /src/public/upload_obfuscated.js /src/public/upload.js
COPY --from=node_builder /src/public/validatePassword_obfuscated.js /src/public/validatePassword.js COPY --from=node_builder /src/public/validatePassword_obfuscated.js /src/public/validatePassword.js
RUN apk update && apk upgrade && apk add --no-cache ca-certificates RUN apk update && apk upgrade && apk add --no-cache ca-certificates tzdata
RUN update-ca-certificates RUN update-ca-certificates
RUN go install github.com/a-h/templ/cmd/templ@$(go list -m -f '{{ .Version }}' github.com/a-h/templ) RUN go install github.com/a-h/templ/cmd/templ@$(go list -m -f '{{ .Version }}' github.com/a-h/templ)
RUN templ generate RUN templ generate
@ -30,9 +32,12 @@ FROM scratch
WORKDIR /src WORKDIR /src
COPY --from=go_builder /usr/share/zoneinfo /usr/share/zoneinfo
COPY --from=go_builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=go_builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=go_builder /src/schema.sql /src COPY --from=go_builder /src/schema.sql /src
COPY --from=go_builder /src/public /src/public COPY --from=go_builder /src/public /src/public
COPY --from=go_builder /src/tmp/main /src COPY --from=go_builder /src/tmp/main /src
ENV TZ Asia/Jakarta
ENTRYPOINT ["./main"] ENTRYPOINT ["./main"]

View File

@ -1,6 +1,7 @@
package indexHandler package indexHandler
import ( import (
"github.com/fossyy/filekeeper/session"
"net/http" "net/http"
"github.com/fossyy/filekeeper/logger" "github.com/fossyy/filekeeper/logger"
@ -14,7 +15,8 @@ func init() {
} }
func GET(w http.ResponseWriter, r *http.Request) { func GET(w http.ResponseWriter, r *http.Request) {
component := indexView.Main("main page") _, userSession, _ := session.GetSession(r)
component := indexView.Main("main page", userSession)
err := component.Render(r.Context(), w) err := component.Render(r.Context(), w)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)

View File

@ -17,7 +17,7 @@ func init() {
func GET(w http.ResponseWriter, r *http.Request) { func GET(w http.ResponseWriter, r *http.Request) {
userSession := r.Context().Value("user").(types.User) userSession := r.Context().Value("user").(types.User)
component := userView.Main("User Page", userSession.Email, userSession.Username, session.UserSessionInfoList[userSession.Email]) component := userView.Main("User Page", userSession.Email, userSession.Username, session.GetSessions(userSession.Email))
err := component.Render(r.Context(), w) err := component.Render(r.Context(), w)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)

View File

@ -59,10 +59,13 @@ func Handler(next http.Handler) http.Handler {
} }
func Auth(next http.HandlerFunc, w http.ResponseWriter, r *http.Request) { func Auth(next http.HandlerFunc, w http.ResponseWriter, r *http.Request) {
status, user := session.GetSession(r) status, user, sessionID := session.GetSession(r)
switch status { switch status {
case session.Authorized: case session.Authorized:
userSession := session.GetSessionInfo(user.Email, sessionID)
userSession.UpdateAccessTime()
ctx := context.WithValue(r.Context(), "user", user) ctx := context.WithValue(r.Context(), "user", user)
req := r.WithContext(ctx) req := r.WithContext(ctx)
r.Context().Value("user") r.Context().Value("user")
@ -94,7 +97,7 @@ func Auth(next http.HandlerFunc, w http.ResponseWriter, r *http.Request) {
} }
func Guest(next http.HandlerFunc, w http.ResponseWriter, r *http.Request) { func Guest(next http.HandlerFunc, w http.ResponseWriter, r *http.Request) {
status, _ := session.GetSession(r) status, _, _ := session.GetSession(r)
switch status { switch status {
case session.Authorized: case session.Authorized:

1
public/brand.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -40,10 +40,8 @@ const (
InvalidSession UserStatus = "invalid_session" InvalidSession UserStatus = "invalid_session"
) )
type SessionInfoList map[string][]*SessionInfo
var GlobalSessionStore = SessionStore{Sessions: make(map[string]*Session)} var GlobalSessionStore = SessionStore{Sessions: make(map[string]*Session)}
var UserSessionInfoList = make(SessionInfoList) var UserSessionInfoList = make(map[string]map[string]*SessionInfo)
type SessionNotFoundError struct{} type SessionNotFoundError struct{}
@ -95,22 +93,22 @@ func (s *Session) Destroy(w http.ResponseWriter) {
} }
func AddSessionInfo(email string, sessionInfo *SessionInfo) { func AddSessionInfo(email string, sessionInfo *SessionInfo) {
UserSessionInfoList[email] = append(UserSessionInfoList[email], sessionInfo) if _, ok := UserSessionInfoList[email]; !ok {
UserSessionInfoList[email] = make(map[string]*SessionInfo)
}
UserSessionInfoList[email][sessionInfo.SessionID] = sessionInfo
} }
func RemoveSessionInfo(email string, id string) { func RemoveSessionInfo(email string, id string) {
sessionInfos := UserSessionInfoList[email] if userSessions, ok := UserSessionInfoList[email]; ok {
var updatedSessionInfos []*SessionInfo if _, ok := userSessions[id]; ok {
for _, sessionInfo := range sessionInfos { delete(userSessions, id)
if sessionInfo.SessionID != id { if len(userSessions) == 0 {
updatedSessionInfos = append(updatedSessionInfos, sessionInfo)
}
}
if len(updatedSessionInfos) > 0 {
UserSessionInfoList[email] = updatedSessionInfos
return
}
delete(UserSessionInfoList, email) delete(UserSessionInfoList, email)
}
}
}
} }
func RemoveAllSessions(email string) { func RemoveAllSessions(email string) {
@ -122,8 +120,8 @@ func RemoveAllSessions(email string) {
} }
func GetSessionInfo(email string, id string) *SessionInfo { func GetSessionInfo(email string, id string) *SessionInfo {
for _, sessionInfo := range UserSessionInfoList[email] { if userSession, ok := UserSessionInfoList[email]; ok {
if sessionInfo.SessionID == id { if sessionInfo, ok := userSession[id]; ok {
return sessionInfo return sessionInfo
} }
} }
@ -136,26 +134,37 @@ func (sessionInfo *SessionInfo) UpdateAccessTime() {
sessionInfo.AccessAt = formattedTime sessionInfo.AccessAt = formattedTime
} }
func GetSession(r *http.Request) (UserStatus, types.User) { func GetSession(r *http.Request) (UserStatus, types.User, string) {
cookie, err := r.Cookie("Session") cookie, err := r.Cookie("Session")
if err != nil { if err != nil {
return Unauthorized, types.User{} return Unauthorized, types.User{}, ""
} }
storeSession, err := GlobalSessionStore.Get(cookie.Value) storeSession, err := GlobalSessionStore.Get(cookie.Value)
if err != nil { if err != nil {
if errors.Is(err, &SessionNotFoundError{}) { if errors.Is(err, &SessionNotFoundError{}) {
return InvalidSession, types.User{} return InvalidSession, types.User{}, ""
} }
return Unauthorized, types.User{} return Unauthorized, types.User{}, ""
} }
val := storeSession.Values["user"] val := storeSession.Values["user"]
var userSession = types.User{} var userSession = types.User{}
userSession, ok := val.(types.User) userSession, ok := val.(types.User)
if !ok { if !ok {
return Unauthorized, types.User{} return Unauthorized, types.User{}, ""
} }
return Authorized, userSession return Authorized, userSession, cookie.Value
}
func GetSessions(email string) []*SessionInfo {
if sessions, ok := UserSessionInfoList[email]; ok {
result := make([]*SessionInfo, 0, len(sessions))
for _, sessionInfo := range sessions {
result = append(result, sessionInfo)
}
return result
}
return nil
} }

View File

@ -26,10 +26,6 @@ type Env struct {
var env *Env var env *Env
var log *logger.AggregatedLogger var log *logger.AggregatedLogger
const (
csrfTokenLength = 32 // Length of the CSRF token in bytes
)
func init() { func init() {
env = &Env{value: map[string]string{}} env = &Env{value: map[string]string{}}
} }

View File

@ -11,8 +11,8 @@ templ form(err types.Message, title string) {
<div class="mx-auto w-full max-w-md space-y-8"> <div class="mx-auto w-full max-w-md space-y-8">
<header class="text-center"> <header class="text-center">
<div class="space-y-2"> <div class="space-y-2">
<h1 class="text-3xl font-bold text-white">Sign Up</h1> <h1 class="text-3xl font-bold text-white">Set Up Your Account</h1>
<p class="text-gray-500 dark:text-gray-400">Enter your email below to login to your account</p> <p class="text-gray-500 dark:text-gray-400">Enter your information to create a new account</p>
switch err.Code { switch err.Code {
case 0: case 0:
<div class="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert"> <div class="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert">

View File

@ -1,26 +1,72 @@
package indexView package indexView
import ( import (
"github.com/fossyy/filekeeper/types"
"github.com/fossyy/filekeeper/view/layout" "github.com/fossyy/filekeeper/view/layout"
) )
templ content(title string) { templ content(title string, user types.User) {
@layout.Base(title){ @layout.Base(title){
<div class="bg-white"> <div class="bg-white">
<nav class="container mx-auto px-6 py-4 flex justify-between items-center"> <header class="flex items-center justify-between border-b border-gray-200 bg-white px-6 py-4">
<a href="#" class="text-blue-500 text-xl font-semibold"> <div class="flex items-center gap-4">
C. <a class="flex items-center gap-2" href="#">
<image src="public/brand.svg" width="48" height="48"/>
<span class="text-lg font-semibold">Filekeeper</span>
</a> </a>
</div>
<div class="flex space-x-4"> <div class="flex space-x-4">
if user.Authenticated {
<div class="flex items-center gap-4">
<div
class="relative inline-flex items-center justify-center w-10 h-10 overflow-hidden bg-gray-100 rounded-full">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
width="256" height="256" viewBox="0 0 256 256" xml:space="preserve">
<defs>
</defs>
<g style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: none; fill-rule: nonzero; opacity: 1;"
transform="translate(1.4065934065934016 1.4065934065934016) scale(2.81 2.81)">
<circle cx="45" cy="45" r="44"
style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(178,178,178); fill-rule: nonzero; opacity: 1;"
transform=" matrix(1 0 0 1 0 0) " />
<circle cx="44.997" cy="39.727000000000004" r="19.817"
style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(109,109,109); fill-rule: nonzero; opacity: 1;"
transform=" matrix(1 0 0 1 0 0) " />
<path
d="M 11.266 73.25 C 19.337 63.622 31.454 57.5 45 57.5 c 13.546 0 25.663 6.122 33.734 15.75 l 0 0 C 70.663 82.878 58.547 89 45 89 C 31.454 89 19.337 82.878 11.266 73.25 L 11.266 73.25 z"
style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(109,109,109); fill-rule: nonzero; opacity: 1;"
transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
<path
d="M 45 90 C 20.187 90 0 69.813 0 45 C 0 20.187 20.187 0 45 0 c 24.813 0 45 20.187 45 45 C 90 69.813 69.813 90 45 90 z M 45 2 C 21.29 2 2 21.29 2 45 c 0 23.71 19.29 43 43 43 c 23.71 0 43 -19.29 43 -43 C 88 21.29 68.71 2 45 2 z"
style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(43,43,43); fill-rule: nonzero; opacity: 1;"
transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
<path
d="M 78.734 73.25 c -6.576 -7.844 -15.837 -13.358 -26.368 -15.133 c 7.294 -2.925 12.451 -10.048 12.451 -18.387 c 0 -10.945 -8.873 -19.817 -19.817 -19.817 S 25.183 28.785 25.183 39.73 c 0 8.339 5.157 15.462 12.451 18.387 c -10.531 1.775 -19.793 7.29 -26.368 15.133 v 0 C 19.337 82.878 31.454 89 45 89 C 58.547 89 70.663 82.878 78.734 73.25 L 78.734 73.25 z"
style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(109,109,109); fill-rule: nonzero; opacity: 1;"
transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
<path
d="M 45 90 c -13.344 0 -25.919 -5.871 -34.5 -16.107 L 9.961 73.25 l 0.539 -0.643 c 6.239 -7.441 14.692 -12.654 24.046 -14.883 c -6.379 -3.687 -10.363 -10.467 -10.363 -17.995 c 0 -11.479 9.339 -20.817 20.817 -20.817 s 20.817 9.339 20.817 20.817 c 0 7.528 -3.983 14.309 -10.362 17.995 c 9.354 2.229 17.808 7.441 24.046 14.883 l 0.538 0.643 l -0.538 0.643 C 70.919 84.129 58.344 90 45 90 z M 12.581 73.25 C 20.764 82.635 32.531 88 45 88 c 12.47 0 24.236 -5.365 32.419 -14.75 C 70.887 65.761 61.964 60.748 52.2 59.104 l -3.506 -0.591 l 3.3 -1.323 c 7.183 -2.882 11.823 -9.734 11.823 -17.46 c 0 -10.376 -8.441 -18.817 -18.817 -18.817 s -18.817 8.441 -18.817 18.817 c 0 7.726 4.641 14.578 11.823 17.46 l 3.3 1.323 L 37.8 59.104 C 28.037 60.748 19.114 65.76 12.581 73.25 z"
style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(43,43,43); fill-rule: nonzero; opacity: 1;"
transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
</g>
</svg>
</div>
<div class="font-medium hidden sm:block">
<div>{ user.Username }</div>
<div class="text-sm text-gray-500">{ user.Email }</div>
</div>
</div>
} else {
<a href="/signup" class="text-gray-600 hover:text-gray-800"> <a href="/signup" class="text-gray-600 hover:text-gray-800">
Sign up Sign up
</a> </a>
<a href="/signin" class="text-gray-600 hover:text-gray-800"> <a href="/signin" class="text-gray-600 hover:text-gray-800">
Sign in Sign in
</a> </a>
}
</div> </div>
</nav> </header>
<header class="container mx-auto px-6 py-16 text-center"> <main class="container mx-auto px-6 py-16 text-center">
<h1 class="text-5xl font-bold text-gray-900 mb-2">Your files, always within reach.</h1> <h1 class="text-5xl font-bold text-gray-900 mb-2">Your files, always within reach.</h1>
<p class="text-gray-700 text-lg mb-8"> <p class="text-gray-700 text-lg mb-8">
Store, access, and share your files from anywhere. We offer secure and reliable file storage, so you can Store, access, and share your files from anywhere. We offer secure and reliable file storage, so you can
@ -29,75 +75,128 @@ templ content(title string) {
<div class="flex justify-center items-center space-x-4"> <div class="flex justify-center items-center space-x-4">
<div class="sm:flex sm:justify-center lg:justify-start"> <div class="sm:flex sm:justify-center lg:justify-start">
<div class="rounded-md shadow"> <div class="rounded-md shadow">
if user.Authenticated {
<a class="w-full flex items-center justify-center px-8 py-3 text-base leading-6 font-medium rounded-md text-white bg-pink-400 hover:bg-pink-500 hover:text-white focus:ring ring-offset-2 ring-pink-400 focus:outline-none transition duration-150 ease-in-out md:py-4 md:text-lg md:px-10"
href="/user">
Open Dashboard
</a>
} else {
<a class="w-full flex items-center justify-center px-8 py-3 text-base leading-6 font-medium rounded-md text-white bg-pink-400 hover:bg-pink-500 hover:text-white focus:ring ring-offset-2 ring-pink-400 focus:outline-none transition duration-150 ease-in-out md:py-4 md:text-lg md:px-10" <a class="w-full flex items-center justify-center px-8 py-3 text-base leading-6 font-medium rounded-md text-white bg-pink-400 hover:bg-pink-500 hover:text-white focus:ring ring-offset-2 ring-pink-400 focus:outline-none transition duration-150 ease-in-out md:py-4 md:text-lg md:px-10"
href="/signup"> href="/signup">
Get started Get started
</a> </a>
}
</div> </div>
</div> </div>
</div> </div>
<div class="flex justify-center items-center space-x-4 mt-8">
<div class="grid gap-4 md:gap-8 lg:gap-4 items-start sm:grid-cols-2"> <section class="w-full py-12 md:py-24 lg:py-32">
<div class="flex items-center space-x-4"> <div class="container px-4 md:px-6">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" <div class="grid gap-8 sm:grid-cols-2 md:grid-cols-3">
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" <div class="grid gap-1 items-center">
class="w-6 h-6 flex-shrink-0 text-gray-500"> <div class="flex justify-center">
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path> <svg
<polyline points="22 4 12 14.01 9 11.01"></polyline> xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="h-8 w-8 text-gray-900"
>
<path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z"></path>
</svg> </svg>
<div class="text-sm font-medium leading-none md:text-base">
Secure encryption to protect your data
</div> </div>
<h3 class="text-lg font-bold">Unlimited Storage</h3>
<p class="text-gray-500">
Store as many files as you need with our generous storage limits.
</p>
</div> </div>
<div class="flex items-center space-x-4"> <div class="grid gap-1 items-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" <div class="flex justify-center">
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" <svg
class="w-6 h-6 flex-shrink-0 text-gray-500"> xmlns="http://www.w3.org/2000/svg"
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path> width="24"
<polyline points="22 4 12 14.01 9 11.01"></polyline> height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="h-8 w-8 text-gray-900"
>
<rect width="18" height="11" x="3" y="11" rx="2" ry="2"></rect>
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
</svg> </svg>
<div class="text-sm font-medium leading-none md:text-base">
Easy file sharing with customizable permissions
</div> </div>
<h3 class="text-lg font-bold">Secure Encryption</h3>
<p class="text-gray-500">
Your files are encrypted with the latest security protocols to keep them safe.
</p>
</div> </div>
<div class="flex items-center space-x-4"> <div class="grid gap-1 items-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" <div class="flex justify-center">
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" <svg
class="w-6 h-6 flex-shrink-0 text-gray-500"> xmlns="http://www.w3.org/2000/svg"
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path> width="24"
<polyline points="22 4 12 14.01 9 11.01"></polyline> height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="h-8 w-8 text-gray-900"
>
<path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"></path>
<polyline points="16 6 12 2 8 6"></polyline>
<line x1="12" x2="12" y1="2" y2="15"></line>
</svg> </svg>
<div class="text-sm font-medium leading-none md:text-base">
Unlimited storage capacity for your files
</div> </div>
</div> <h3 class="text-lg font-bold">Easy Sharing</h3>
<div class="flex items-center space-x-4"> <p class="text-gray-500">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" Quickly share files with friends, family, or colleagues with shareable links.
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" </p>
class="w-6 h-6 flex-shrink-0 text-gray-500">
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
<polyline points="22 4 12 14.01 9 11.01"></polyline>
</svg>
<div class="text-sm font-medium leading-none md:text-base">
Seamless collaboration with built-in productivity tools
</div> </div>
</div> </div>
</div> </div>
</section>
</main>
</div> </div>
</header>
</div> <footer class="bg-white p-6 md:p-8 w-full relative bottom-0 border-t border-gray-200 w-full py-8">
<footer class="w-full border-t"> <div class="container mx-auto flex flex-col items-center justify-between gap-6 md:flex-row">
<div class="container grid-inset py-12 gap-4 text-center md:gap-8 md:py-16"> <div class="flex items-center gap-2">
<div class="flex items-center justify-center gap-2 text-xs tracking-wide md:gap-4"> <image src="public/brand.svg" width="48" height="48"/>
<p class="text-gray-500 dark:text-gray-400">© 2023 Acme, Inc. All rights reserved.</p> <span class="text-lg font-semibold">Filekeeper</span>
<p class="text-gray-500 dark:text-gray-400">Terms of Service</p>
<p class="text-gray-500 dark:text-gray-400">Privacy Policy</p>
</div> </div>
<nav class="flex flex-wrap items-center justify-center gap-4 text-sm font-medium">
<a class="hover:underline" href="#">
Pricing
</a>
<a class="hover:underline" href="#">
About
</a>
<a class="hover:underline" href="#">
Contact
</a>
<a class="hover:underline" href="#">
Terms
</a>
<a class="hover:underline" href="#">
Privacy
</a>
</nav>
<p class="text-sm text-gray-500">© 2024 Filekeeper. All rights reserved.</p>
</div> </div>
</footer> </footer>
} }
} }
templ Main(title string) { templ Main(title string, user types.User) {
@content(title) @content(title, user)
} }

View File

@ -12,7 +12,7 @@ templ form(err types.Message, title string) {
<header class="text-center"> <header class="text-center">
<div class="space-y-2"> <div class="space-y-2">
<h1 class="text-3xl font-bold text-white">Sign Up</h1> <h1 class="text-3xl font-bold text-white">Sign Up</h1>
<p class="text-gray-500 dark:text-gray-400">Enter your email below to login to your account</p> <p class="text-gray-500 dark:text-gray-400">Enter your information to create an account</p>
switch err.Code { switch err.Code {
case 0: case 0:
<div class="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert"> <div class="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert">

View File

@ -218,16 +218,12 @@ templ content(email string, username string, title string, ListSession []*sessio
</div> </div>
<a <a
class="hover:bg-gray-200 inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 px-4 py-2" class="hover:bg-gray-200 inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 px-4 py-2"
type="button" id="radix-:rq:" aria-haspopup="menu" type="button" href="/upload">
aria-expanded="false" data-state="closed"
href="/upload">
Upload Upload
</a> </a>
<a <a
class="hover:bg-gray-200 inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 px-4 py-2" class="hover:bg-gray-200 inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 px-4 py-2"
type="button" id="radix-:rq:" aria-haspopup="menu" type="button" href="/download">
aria-expanded="false" data-state="closed"
href="/download">
Download Download
</a> </a>
</div> </div>
@ -241,18 +237,20 @@ templ content(email string, username string, title string, ListSession []*sessio
<div class="grid gap-2"> <div class="grid gap-2">
<h3 class="text-lg font-semibold">Pro Plan</h3> <h3 class="text-lg font-semibold">Pro Plan</h3>
<p class="text-gray-500">50GB of storage for $9.99/month</p> <p class="text-gray-500">50GB of storage for $9.99/month</p>
<button <a
class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4 py-2"> class="hover:bg-gray-200 inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 px-4 py-2"
type="button" href="#">
Upgrade Upgrade
</button> </a>
</div> </div>
<div class="grid gap-2"> <div class="grid gap-2">
<h3 class="text-lg font-semibold">Enterprise Plan</h3> <h3 class="text-lg font-semibold">Enterprise Plan</h3>
<p class="text-gray-500">1TB of storage for $49.99/month</p> <p class="text-gray-500">1TB of storage for $49.99/month</p>
<button <a
class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4 py-2"> class="hover:bg-gray-200 inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 px-4 py-2"
type="button" href="#">
Upgrade Upgrade
</button> </a>
</div> </div>
</div> </div>
</div> </div>