You've already forked go-url-shortener
render html change templ
This commit is contained in:
BIN
internal/.DS_Store
vendored
Normal file
BIN
internal/.DS_Store
vendored
Normal file
Binary file not shown.
31
internal/db/db.go
Normal file
31
internal/db/db.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.24.0
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type DBTX interface {
|
||||
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
|
||||
PrepareContext(context.Context, string) (*sql.Stmt, error)
|
||||
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
|
||||
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
|
||||
}
|
||||
|
||||
func New(db DBTX) *Queries {
|
||||
return &Queries{db: db}
|
||||
}
|
||||
|
||||
type Queries struct {
|
||||
db DBTX
|
||||
}
|
||||
|
||||
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
|
||||
return &Queries{
|
||||
db: tx,
|
||||
}
|
||||
}
|
||||
27
internal/db/models.go
Normal file
27
internal/db/models.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.24.0
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
HashedPassword string `json:"hashed_password"`
|
||||
Email string `json:"email"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
type UserRelateUrl struct {
|
||||
ID int64 `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
ShortUrl string `json:"short_url"`
|
||||
OriginUrl string `json:"origin_url"`
|
||||
Status int32 `json:"status"`
|
||||
ExpireAt time.Time `json:"expire_at"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
24
internal/db/querier.go
Normal file
24
internal/db/querier.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.24.0
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type Querier interface {
|
||||
CreateUser(ctx context.Context, arg *CreateUserParams) (*User, error)
|
||||
CreateUserUrl(ctx context.Context, arg *CreateUserUrlParams) (*UserRelateUrl, error)
|
||||
DeleteUser(ctx context.Context, id string) error
|
||||
GetUser(ctx context.Context, id string) (*User, error)
|
||||
GetUserByEmail(ctx context.Context, email string) (*User, error)
|
||||
GetUserByName(ctx context.Context, username string) (*User, error)
|
||||
ListUrlByUser(ctx context.Context, userID string) ([]*UserRelateUrl, error)
|
||||
ListUsers(ctx context.Context, arg *ListUsersParams) ([]*User, error)
|
||||
UpdateStatus(ctx context.Context, arg *UpdateStatusParams) (*UserRelateUrl, error)
|
||||
UpdateUser(ctx context.Context, arg *UpdateUserParams) (*User, error)
|
||||
}
|
||||
|
||||
var _ Querier = (*Queries)(nil)
|
||||
36
internal/db/query/user.sql
Normal file
36
internal/db/query/user.sql
Normal file
@@ -0,0 +1,36 @@
|
||||
-- name: CreateUser :one
|
||||
INSERT INTO users (
|
||||
id, username, hashed_password, email
|
||||
) VALUES (
|
||||
$1, $2, $3, $4
|
||||
)
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteUser :exec
|
||||
DELETE FROM users
|
||||
WHERE id = $1;
|
||||
|
||||
-- name: UpdateUser :one
|
||||
UPDATE users
|
||||
SET hashed_password = $2,
|
||||
email = $3
|
||||
WHERE id = $1
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetUser :one
|
||||
SELECT * FROM users
|
||||
WHERE id = $1 LIMIT 1;
|
||||
|
||||
-- name: GetUserByName :one
|
||||
SELECT * FROM users
|
||||
WHERE username = $1 LIMIT 1;
|
||||
|
||||
-- name: GetUserByEmail :one
|
||||
SELECT * FROM users
|
||||
WHERE email = $1 LIMIT 1;
|
||||
|
||||
-- name: ListUsers :many
|
||||
SELECT * FROM users
|
||||
ORDER BY id
|
||||
LIMIT $1
|
||||
OFFSET $2;
|
||||
19
internal/db/query/user_relate_url.sql
Normal file
19
internal/db/query/user_relate_url.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
-- name: CreateUserUrl :one
|
||||
INSERT INTO user_relate_url (
|
||||
user_id, short_url, origin_url, status, expire_at
|
||||
) VALUES (
|
||||
$1, $2, $3, 0, $4
|
||||
)
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateStatus :one
|
||||
UPDATE user_relate_url
|
||||
SET status = $2
|
||||
WHERE short_url = $1
|
||||
RETURNING *;
|
||||
|
||||
-- name: ListUrlByUser :many
|
||||
SELECT *
|
||||
FROM user_relate_url
|
||||
WHERE user_id = $1
|
||||
ORDER BY id DESC;
|
||||
2
internal/db/schema/000001_init_schema.down.sql
Normal file
2
internal/db/schema/000001_init_schema.down.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
DROP TABLE "user_relate_url";
|
||||
DROP TABLE "users";
|
||||
27
internal/db/schema/000001_init_schema.up.sql
Normal file
27
internal/db/schema/000001_init_schema.up.sql
Normal file
@@ -0,0 +1,27 @@
|
||||
CREATE TABLE "users" (
|
||||
"id" varchar NOT NULL PRIMARY KEY,
|
||||
"username" varchar NOT NULL,
|
||||
"hashed_password" varchar NOT NULL,
|
||||
"email" varchar NOT NULL,
|
||||
"created_at" timestamptz NOT NULL DEFAULT (now())
|
||||
);
|
||||
|
||||
ALTER TABLE "users" ADD CONSTRAINT "username_key" UNIQUE ("username");
|
||||
ALTER TABLE "users" ADD CONSTRAINT "email_key" UNIQUE ("email");
|
||||
CREATE INDEX ON "users" ("username");
|
||||
CREATE INDEX ON "users" ("email");
|
||||
|
||||
|
||||
CREATE TABLE "user_relate_url" (
|
||||
"id" bigserial NOT NULL PRIMARY KEY,
|
||||
"user_id" varchar NOT NULL,
|
||||
"short_url" varchar NOT NULL,
|
||||
"origin_url" varchar NOT NULL,
|
||||
"status" int NOT NULL DEFAULT 0,
|
||||
"expire_at" timestamptz NOT NULL,
|
||||
"created_at" timestamptz NOT NULL DEFAULT (now())
|
||||
);
|
||||
|
||||
ALTER TABLE "user_relate_url" ADD CONSTRAINT "short_url_key" UNIQUE ("short_url");
|
||||
CREATE INDEX ON "user_relate_url" ("user_id");
|
||||
CREATE INDEX ON "user_relate_url" ("short_url");
|
||||
61
internal/db/store.go
Normal file
61
internal/db/store.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
type Store interface {
|
||||
Querier
|
||||
ExecTx(ctx context.Context, fn func(*Queries) error) error
|
||||
IsUniqueViolation(err error) bool
|
||||
IsForeignKeyViolation(err error) bool
|
||||
IsNoRows(err error) bool
|
||||
}
|
||||
|
||||
type SQLStore struct {
|
||||
db *sql.DB
|
||||
*Queries
|
||||
}
|
||||
|
||||
func NewStore(db *sql.DB) Store {
|
||||
return &SQLStore{
|
||||
db: db,
|
||||
Queries: New(db),
|
||||
}
|
||||
}
|
||||
|
||||
func (store *SQLStore) ExecTx(ctx context.Context, fn func(*Queries) error) error {
|
||||
tx, err := store.db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
q := New(tx)
|
||||
err = fn(q)
|
||||
if err != nil {
|
||||
if rbErr := tx.Rollback(); rbErr != nil {
|
||||
return fmt.Errorf("tx err: %v, rb err: %v", err, rbErr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (store *SQLStore) IsUniqueViolation(err error) bool {
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
return ok && pqErr.Code == "23505"
|
||||
}
|
||||
|
||||
func (store *SQLStore) IsForeignKeyViolation(err error) bool {
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
return ok && pqErr.Code == "23503"
|
||||
}
|
||||
|
||||
func (store *SQLStore) IsNoRows(err error) bool {
|
||||
return err == sql.ErrNoRows
|
||||
}
|
||||
176
internal/db/user.sql.go
Normal file
176
internal/db/user.sql.go
Normal file
@@ -0,0 +1,176 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.24.0
|
||||
// source: user.sql
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const createUser = `-- name: CreateUser :one
|
||||
INSERT INTO users (
|
||||
id, username, hashed_password, email
|
||||
) VALUES (
|
||||
$1, $2, $3, $4
|
||||
)
|
||||
RETURNING id, username, hashed_password, email, created_at
|
||||
`
|
||||
|
||||
type CreateUserParams struct {
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
HashedPassword string `json:"hashed_password"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateUser(ctx context.Context, arg *CreateUserParams) (*User, error) {
|
||||
row := q.db.QueryRowContext(ctx, createUser,
|
||||
arg.ID,
|
||||
arg.Username,
|
||||
arg.HashedPassword,
|
||||
arg.Email,
|
||||
)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Username,
|
||||
&i.HashedPassword,
|
||||
&i.Email,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return &i, err
|
||||
}
|
||||
|
||||
const deleteUser = `-- name: DeleteUser :exec
|
||||
DELETE FROM users
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteUser(ctx context.Context, id string) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteUser, id)
|
||||
return err
|
||||
}
|
||||
|
||||
const getUser = `-- name: GetUser :one
|
||||
SELECT id, username, hashed_password, email, created_at FROM users
|
||||
WHERE id = $1 LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUser(ctx context.Context, id string) (*User, error) {
|
||||
row := q.db.QueryRowContext(ctx, getUser, id)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Username,
|
||||
&i.HashedPassword,
|
||||
&i.Email,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return &i, err
|
||||
}
|
||||
|
||||
const getUserByEmail = `-- name: GetUserByEmail :one
|
||||
SELECT id, username, hashed_password, email, created_at FROM users
|
||||
WHERE email = $1 LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserByEmail(ctx context.Context, email string) (*User, error) {
|
||||
row := q.db.QueryRowContext(ctx, getUserByEmail, email)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Username,
|
||||
&i.HashedPassword,
|
||||
&i.Email,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return &i, err
|
||||
}
|
||||
|
||||
const getUserByName = `-- name: GetUserByName :one
|
||||
SELECT id, username, hashed_password, email, created_at FROM users
|
||||
WHERE username = $1 LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserByName(ctx context.Context, username string) (*User, error) {
|
||||
row := q.db.QueryRowContext(ctx, getUserByName, username)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Username,
|
||||
&i.HashedPassword,
|
||||
&i.Email,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return &i, err
|
||||
}
|
||||
|
||||
const listUsers = `-- name: ListUsers :many
|
||||
SELECT id, username, hashed_password, email, created_at FROM users
|
||||
ORDER BY id
|
||||
LIMIT $1
|
||||
OFFSET $2
|
||||
`
|
||||
|
||||
type ListUsersParams struct {
|
||||
Limit int32 `json:"limit"`
|
||||
Offset int32 `json:"offset"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListUsers(ctx context.Context, arg *ListUsersParams) ([]*User, error) {
|
||||
rows, err := q.db.QueryContext(ctx, listUsers, arg.Limit, arg.Offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []*User{}
|
||||
for rows.Next() {
|
||||
var i User
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Username,
|
||||
&i.HashedPassword,
|
||||
&i.Email,
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, &i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateUser = `-- name: UpdateUser :one
|
||||
UPDATE users
|
||||
SET hashed_password = $2,
|
||||
email = $3
|
||||
WHERE id = $1
|
||||
RETURNING id, username, hashed_password, email, created_at
|
||||
`
|
||||
|
||||
type UpdateUserParams struct {
|
||||
ID string `json:"id"`
|
||||
HashedPassword string `json:"hashed_password"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUser(ctx context.Context, arg *UpdateUserParams) (*User, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateUser, arg.ID, arg.HashedPassword, arg.Email)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Username,
|
||||
&i.HashedPassword,
|
||||
&i.Email,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return &i, err
|
||||
}
|
||||
112
internal/db/user_relate_url.sql.go
Normal file
112
internal/db/user_relate_url.sql.go
Normal file
@@ -0,0 +1,112 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.24.0
|
||||
// source: user_relate_url.sql
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
const createUserUrl = `-- name: CreateUserUrl :one
|
||||
INSERT INTO user_relate_url (
|
||||
user_id, short_url, origin_url, status, expire_at
|
||||
) VALUES (
|
||||
$1, $2, $3, 0, $4
|
||||
)
|
||||
RETURNING id, user_id, short_url, origin_url, status, expire_at, created_at
|
||||
`
|
||||
|
||||
type CreateUserUrlParams struct {
|
||||
UserID string `json:"user_id"`
|
||||
ShortUrl string `json:"short_url"`
|
||||
OriginUrl string `json:"origin_url"`
|
||||
ExpireAt time.Time `json:"expire_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateUserUrl(ctx context.Context, arg *CreateUserUrlParams) (*UserRelateUrl, error) {
|
||||
row := q.db.QueryRowContext(ctx, createUserUrl,
|
||||
arg.UserID,
|
||||
arg.ShortUrl,
|
||||
arg.OriginUrl,
|
||||
arg.ExpireAt,
|
||||
)
|
||||
var i UserRelateUrl
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.ShortUrl,
|
||||
&i.OriginUrl,
|
||||
&i.Status,
|
||||
&i.ExpireAt,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return &i, err
|
||||
}
|
||||
|
||||
const listUrlByUser = `-- name: ListUrlByUser :many
|
||||
SELECT id, user_id, short_url, origin_url, status, expire_at, created_at
|
||||
FROM user_relate_url
|
||||
WHERE user_id = $1
|
||||
ORDER BY id DESC
|
||||
`
|
||||
|
||||
func (q *Queries) ListUrlByUser(ctx context.Context, userID string) ([]*UserRelateUrl, error) {
|
||||
rows, err := q.db.QueryContext(ctx, listUrlByUser, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []*UserRelateUrl{}
|
||||
for rows.Next() {
|
||||
var i UserRelateUrl
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.ShortUrl,
|
||||
&i.OriginUrl,
|
||||
&i.Status,
|
||||
&i.ExpireAt,
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, &i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateStatus = `-- name: UpdateStatus :one
|
||||
UPDATE user_relate_url
|
||||
SET status = $2
|
||||
WHERE short_url = $1
|
||||
RETURNING id, user_id, short_url, origin_url, status, expire_at, created_at
|
||||
`
|
||||
|
||||
type UpdateStatusParams struct {
|
||||
ShortUrl string `json:"short_url"`
|
||||
Status int32 `json:"status"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateStatus(ctx context.Context, arg *UpdateStatusParams) (*UserRelateUrl, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateStatus, arg.ShortUrl, arg.Status)
|
||||
var i UserRelateUrl
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.ShortUrl,
|
||||
&i.OriginUrl,
|
||||
&i.Status,
|
||||
&i.ExpireAt,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return &i, err
|
||||
}
|
||||
172
internal/handler/account.go
Normal file
172
internal/handler/account.go
Normal file
@@ -0,0 +1,172 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/zhang2092/go-url-shortener/internal/db"
|
||||
"github.com/zhang2092/go-url-shortener/internal/middleware"
|
||||
"github.com/zhang2092/go-url-shortener/internal/pkg/cookie"
|
||||
pwd "github.com/zhang2092/go-url-shortener/internal/pkg/password"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/model"
|
||||
)
|
||||
|
||||
func RegisterView(w http.ResponseWriter, r *http.Request) {
|
||||
templ.Register(w, r, &model.RegisterPageData{})
|
||||
}
|
||||
|
||||
func Register(store db.Store) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
email := r.PostFormValue("email")
|
||||
username := r.PostFormValue("username")
|
||||
password := r.PostFormValue("password")
|
||||
resp, ok := viladatorRegister(email, username, password)
|
||||
if !ok {
|
||||
templ.Register(w, r, resp)
|
||||
return
|
||||
}
|
||||
|
||||
hashedPassword, err := pwd.BcryptHashPassword(password)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
arg := &db.CreateUserParams{
|
||||
ID: uuid.Must(uuid.NewRandom()).String(),
|
||||
Username: username,
|
||||
HashedPassword: hashedPassword,
|
||||
Email: email,
|
||||
}
|
||||
|
||||
_, err = store.CreateUser(r.Context(), arg)
|
||||
if err != nil {
|
||||
if store.IsUniqueViolation(err) {
|
||||
resp.Summary = "邮箱或名称已经存在"
|
||||
templ.Register(w, r, resp)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Summary = "请求网络错误,请刷新重试"
|
||||
templ.Register(w, r, resp)
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/login", http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
func LoginView(w http.ResponseWriter, r *http.Request) {
|
||||
templ.Login(w, r, &model.LoginPageData{})
|
||||
}
|
||||
|
||||
func Login(store db.Store) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
|
||||
if err := r.ParseForm(); err != nil {
|
||||
templ.Login(w, r, &model.LoginPageData{Summary: "请求网络错误,请刷新重试"})
|
||||
return
|
||||
}
|
||||
|
||||
email := r.PostFormValue("email")
|
||||
password := r.PostFormValue("password")
|
||||
resp, ok := viladatorLogin(email, password)
|
||||
if !ok {
|
||||
templ.Login(w, r, resp)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
user, err := store.GetUserByEmail(ctx, email)
|
||||
if err != nil {
|
||||
if store.IsNoRows(sql.ErrNoRows) {
|
||||
resp.Summary = "邮箱或密码错误"
|
||||
templ.Login(w, r, resp)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Summary = "请求网络错误,请刷新重试"
|
||||
templ.Login(w, r, resp)
|
||||
return
|
||||
}
|
||||
|
||||
err = pwd.BcryptComparePassword(user.HashedPassword, password)
|
||||
if err != nil {
|
||||
resp.Summary = "邮箱或密码错误"
|
||||
templ.Login(w, r, resp)
|
||||
return
|
||||
}
|
||||
|
||||
encoded, err := middleware.Encode(middleware.AuthorizeCookie, &middleware.Authorize{ID: user.ID, Name: user.Username})
|
||||
if err != nil {
|
||||
resp.Summary = "请求网络错误,请刷新重试(cookie)"
|
||||
templ.Login(w, r, resp)
|
||||
return
|
||||
}
|
||||
|
||||
c := cookie.NewCookie(cookie.AuthorizeName, encoded, time.Now().Add(time.Duration(7200)*time.Second))
|
||||
http.SetCookie(w, c)
|
||||
http.Redirect(w, r, "/", http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
func Logout() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
cookie.DeleteCookie(w, cookie.AuthorizeName)
|
||||
http.Redirect(w, r, "/login", http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
func viladatorRegister(email, username, password string) (*model.RegisterPageData, bool) {
|
||||
ok := true
|
||||
resp := &model.RegisterPageData{
|
||||
Email: email,
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
|
||||
if !ValidateRxEmail(email) {
|
||||
resp.EmailMsg = "请填写正确的邮箱地址"
|
||||
ok = false
|
||||
}
|
||||
if !ValidateRxUsername(username) {
|
||||
resp.UsernameMsg = "名称(6-20,字母,数字)"
|
||||
ok = false
|
||||
}
|
||||
if !ValidatePassword(password) {
|
||||
resp.PasswordMsg = "密码(8-20位)"
|
||||
ok = false
|
||||
}
|
||||
|
||||
return resp, ok
|
||||
}
|
||||
|
||||
func viladatorLogin(email, password string) (*model.LoginPageData, bool) {
|
||||
ok := true
|
||||
errs := &model.LoginPageData{
|
||||
Email: email,
|
||||
Password: password,
|
||||
}
|
||||
|
||||
if !ValidateRxEmail(email) {
|
||||
errs.EmailMsg = "请填写正确的邮箱地址"
|
||||
ok = false
|
||||
}
|
||||
if len(password) == 0 {
|
||||
errs.PasswordMsg = "请填写正确的密码"
|
||||
ok = false
|
||||
}
|
||||
|
||||
return errs, ok
|
||||
}
|
||||
22
internal/handler/home.go
Normal file
22
internal/handler/home.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/zhang2092/go-url-shortener/internal/db"
|
||||
"github.com/zhang2092/go-url-shortener/internal/middleware"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ"
|
||||
)
|
||||
|
||||
func HomeView(store db.Store) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
user := middleware.GetUser(ctx)
|
||||
result, err := store.ListUrlByUser(ctx, user.ID)
|
||||
if err != nil {
|
||||
templ.Home(w, r, nil)
|
||||
return
|
||||
}
|
||||
templ.Home(w, r, result)
|
||||
}
|
||||
}
|
||||
55
internal/handler/resp.go
Normal file
55
internal/handler/resp.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type response struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
|
||||
func respond(w http.ResponseWriter, message string, v any, statusCode int) {
|
||||
rsp := response{
|
||||
Success: true,
|
||||
Message: message,
|
||||
Data: v,
|
||||
}
|
||||
b, err := json.Marshal(rsp)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(statusCode)
|
||||
_, err = w.Write(b)
|
||||
if err != nil && !errors.Is(err, context.Canceled) {
|
||||
log.Printf("could not write http response: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func RespondErr(w http.ResponseWriter, message string, v any) {
|
||||
rsp := response{
|
||||
Success: false,
|
||||
Message: message,
|
||||
Data: v,
|
||||
}
|
||||
b, err := json.Marshal(rsp)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, err = w.Write(b)
|
||||
if err != nil && !errors.Is(err, context.Canceled) {
|
||||
log.Printf("could not write http response: %v\n", err)
|
||||
}
|
||||
}
|
||||
90
internal/handler/short_url.go
Normal file
90
internal/handler/short_url.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/zhang2092/go-url-shortener/internal/db"
|
||||
"github.com/zhang2092/go-url-shortener/internal/middleware"
|
||||
"github.com/zhang2092/go-url-shortener/internal/service"
|
||||
"github.com/zhang2092/go-url-shortener/internal/shortener"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ"
|
||||
)
|
||||
|
||||
func CreateShortUrlView(w http.ResponseWriter, r *http.Request) {
|
||||
templ.CreateUrl(w, r, "")
|
||||
}
|
||||
|
||||
func CreateShortUrl(store db.Store) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
|
||||
if err := r.ParseForm(); err != nil {
|
||||
templ.CreateUrl(w, r, "请求参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
user := middleware.GetUser(ctx)
|
||||
longUrl := r.PostFormValue("long_url")
|
||||
shortUrl, err := shortener.GenerateShortLink(longUrl, user.ID)
|
||||
if err != nil {
|
||||
templ.CreateUrl(w, r, "生成短路径错误")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = store.CreateUserUrl(ctx, &db.CreateUserUrlParams{
|
||||
UserID: user.ID,
|
||||
ShortUrl: shortUrl,
|
||||
OriginUrl: longUrl,
|
||||
ExpireAt: time.Now().Add(time.Hour * 6),
|
||||
})
|
||||
if err != nil {
|
||||
templ.CreateUrl(w, r, "短路径存储错误")
|
||||
return
|
||||
}
|
||||
|
||||
err = service.SaveUrlMapping(shortUrl, longUrl, user.ID)
|
||||
if err != nil {
|
||||
templ.CreateUrl(w, r, "短路径存储错误")
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/", http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteShortUrl(store db.Store) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
shorUrl := vars["shortUrl"]
|
||||
_, err := store.UpdateStatus(r.Context(), &db.UpdateStatusParams{
|
||||
ShortUrl: shorUrl,
|
||||
Status: -1,
|
||||
})
|
||||
if err != nil {
|
||||
RespondErr(w, "删除错误", nil)
|
||||
return
|
||||
}
|
||||
err = service.DeleteShortUrl(shorUrl)
|
||||
if err != nil {
|
||||
RespondErr(w, "删除错误", nil)
|
||||
return
|
||||
}
|
||||
|
||||
respond(w, "删除成功", nil, http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleShortUrlRedirect(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
shorUrl := vars["shortUrl"]
|
||||
link, err := service.RetrieveInitialUrl(shorUrl)
|
||||
if err != nil || len(link) == 0 {
|
||||
RespondErr(w, "短链已经失效", nil)
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(w, r, link, http.StatusFound)
|
||||
}
|
||||
38
internal/handler/validator.go
Normal file
38
internal/handler/validator.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
rxPhone = regexp.MustCompile(`^(13|14|15|16|17|18|19)\d{9}$`)
|
||||
rxEmail = regexp.MustCompile(`\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`)
|
||||
rxUsername = regexp.MustCompile(`^[a-z0-9A-Z]{6,20}$`) // 6到20位(字母,数字)
|
||||
// rxPassword = regexp.MustCompile(`^(?=.*[a-zA-Z])(?=.*[0-9])[A-Za-z0-9]{8,18}$`) // 最少6位,包括至少1个大写字母,1个小写字母,1个数字,1个特殊字符
|
||||
)
|
||||
|
||||
func ValidateRxPhone(phone string) bool {
|
||||
phone = strings.TrimSpace(phone)
|
||||
return rxPhone.MatchString(phone)
|
||||
}
|
||||
|
||||
func ValidateRxEmail(email string) bool {
|
||||
email = strings.TrimSpace(email)
|
||||
return rxEmail.MatchString(email)
|
||||
}
|
||||
|
||||
func ValidateRxUsername(username string) bool {
|
||||
username = strings.TrimSpace(username)
|
||||
return rxUsername.MatchString(username)
|
||||
}
|
||||
|
||||
// func ValidateRxPassword(password string) bool {
|
||||
// password = strings.TrimSpace(password)
|
||||
// return rxPassword.MatchString(password)
|
||||
// }
|
||||
|
||||
func ValidatePassword(password string) bool {
|
||||
password = strings.TrimSpace(password)
|
||||
return len(password) >= 8 && len(password) <= 20
|
||||
}
|
||||
77
internal/middleware/authorize.go
Normal file
77
internal/middleware/authorize.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/securecookie"
|
||||
)
|
||||
|
||||
const (
|
||||
AuthorizeCookie = "authorize"
|
||||
ContextUser ctxKey = "context_user"
|
||||
)
|
||||
|
||||
var secureCookie *securecookie.SecureCookie
|
||||
|
||||
type ctxKey string
|
||||
|
||||
type Authorize struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func SetSecureCookie(sc *securecookie.SecureCookie) {
|
||||
secureCookie = sc
|
||||
}
|
||||
|
||||
func Encode(name string, value any) (string, error) {
|
||||
return secureCookie.Encode(name, value)
|
||||
}
|
||||
|
||||
func MyAuthorize(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
u := GetUser(r.Context())
|
||||
if u == nil {
|
||||
http.Redirect(w, r, "/login", http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func SetUser(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
cookie, err := r.Cookie(AuthorizeCookie)
|
||||
if err != nil {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if cookie == nil || len(cookie.Value) == 0 {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
u := Authorize{}
|
||||
err = secureCookie.Decode(AuthorizeCookie, cookie.Value, &u)
|
||||
if err != nil {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
ctx = context.WithValue(ctx, ContextUser, u)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
func GetUser(ctx context.Context) *Authorize {
|
||||
val := ctx.Value(ContextUser)
|
||||
if u, ok := val.(Authorize); ok {
|
||||
return &u
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
41
internal/pkg/cookie/cookie.go
Normal file
41
internal/pkg/cookie/cookie.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package cookie
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
AuthorizeName = "authorize"
|
||||
)
|
||||
|
||||
func NewCookie(name, value string, expired time.Time) *http.Cookie {
|
||||
return &http.Cookie{
|
||||
Name: name,
|
||||
Value: value,
|
||||
Path: "/",
|
||||
Secure: false, // true->只能https站点操作
|
||||
HttpOnly: true, // true->js不能捕获
|
||||
Expires: expired,
|
||||
}
|
||||
}
|
||||
|
||||
func SetCookie(w http.ResponseWriter, name, value string, expired time.Time) {
|
||||
cookie := NewCookie(name, value, expired)
|
||||
http.SetCookie(w, cookie)
|
||||
}
|
||||
|
||||
func ReadCookie(r *http.Request, name string) (string, error) {
|
||||
cookie, err := r.Cookie(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return cookie.Value, nil
|
||||
}
|
||||
|
||||
func DeleteCookie(w http.ResponseWriter, name string) {
|
||||
cookie := NewCookie(name, "", time.Now().Add(time.Duration(-10)*time.Second))
|
||||
cookie.MaxAge = -1
|
||||
http.SetCookie(w, cookie)
|
||||
}
|
||||
33
internal/pkg/logger/logger.go
Normal file
33
internal/pkg/logger/logger.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"github.com/natefinch/lumberjack"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
var Logger *zap.SugaredLogger
|
||||
|
||||
func NewLogger() {
|
||||
core := zapcore.NewCore(getEncoder(), getLogWriter(), zapcore.DebugLevel)
|
||||
logger := zap.New(core, zap.AddCaller())
|
||||
Logger = logger.Sugar()
|
||||
}
|
||||
|
||||
func getEncoder() zapcore.Encoder {
|
||||
encoderConfig := zap.NewProductionEncoderConfig()
|
||||
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
|
||||
return zapcore.NewConsoleEncoder(encoderConfig)
|
||||
}
|
||||
|
||||
func getLogWriter() zapcore.WriteSyncer {
|
||||
lumberJackLogger := &lumberjack.Logger{
|
||||
Filename: "./log/run.log", // 日志文件的位置
|
||||
MaxSize: 10, // 在进行切割之前,日志文件的最大大小(以MB为单位)
|
||||
MaxBackups: 100, // 保留旧文件的最大个数
|
||||
MaxAge: 365, // 保留旧文件的最大天数
|
||||
Compress: false, // 是否压缩/归档旧文件
|
||||
}
|
||||
return zapcore.AddSync(lumberJackLogger)
|
||||
}
|
||||
79
internal/pkg/password/password.go
Normal file
79
internal/pkg/password/password.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"golang.org/x/crypto/scrypt"
|
||||
)
|
||||
|
||||
// ******************** scrypt ********************
|
||||
|
||||
// ScryptHashPassword scrypt 加密
|
||||
// password 原始密码
|
||||
func ScryptHashPassword(password string) (string, error) {
|
||||
// example for making salt - https://play.golang.org/p/_Aw6WeWC42I
|
||||
salt := make([]byte, 32)
|
||||
_, err := rand.Read(salt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// using recommended cost parameters from - https://godoc.org/golang.org/x/crypto/scrypt
|
||||
shash, err := scrypt.Key([]byte(password), salt, 32768, 8, 1, 32)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// return hex-encoded string with salt appended to password
|
||||
hashedPW := fmt.Sprintf("%s.%s", hex.EncodeToString(shash), hex.EncodeToString(salt))
|
||||
|
||||
return hashedPW, nil
|
||||
}
|
||||
|
||||
// ScryptComparePassword 判断密码是否正确
|
||||
// storedPassword 加密密码
|
||||
// suppliedPassword 原始密码
|
||||
func ScryptComparePassword(storedPassword string, suppliedPassword string) error {
|
||||
pwsalt := strings.Split(storedPassword, ".")
|
||||
|
||||
// check supplied password salted with hash
|
||||
salt, err := hex.DecodeString(pwsalt[1])
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to verify user password")
|
||||
}
|
||||
|
||||
shash, err := scrypt.Key([]byte(suppliedPassword), salt, 32768, 8, 1, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if hex.EncodeToString(shash) == pwsalt[0] {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("password error")
|
||||
}
|
||||
|
||||
// ******************** bcrypt ********************
|
||||
|
||||
// BcryptHashPassword bcrypt 加密
|
||||
// password 原始密码
|
||||
func BcryptHashPassword(password string) (string, error) {
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to hash password: %w", err)
|
||||
}
|
||||
return string(hashedPassword), nil
|
||||
}
|
||||
|
||||
// BcryptComparePassword 判断密码是否正确
|
||||
// hashedPassword 加密密码
|
||||
// password 原始密码
|
||||
func BcryptComparePassword(hashedPassword string, password string) error {
|
||||
return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
|
||||
}
|
||||
58
internal/service/redis_service.go
Normal file
58
internal/service/redis_service.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type StorageService struct {
|
||||
redisClient *redis.Client
|
||||
}
|
||||
|
||||
var (
|
||||
storeService = &StorageService{}
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
const CacheDuration = 6 * time.Hour
|
||||
|
||||
func InitializeStore(addr string, password string, db int) {
|
||||
redisClient := redis.NewClient(&redis.Options{
|
||||
Addr: addr,
|
||||
Password: password,
|
||||
DB: db,
|
||||
})
|
||||
|
||||
pong, err := redisClient.Ping(ctx).Result()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to init redis: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Redis started successfully, ping message = %s\n", pong)
|
||||
storeService.redisClient = redisClient
|
||||
}
|
||||
|
||||
func CloseStoreRedisConn() {
|
||||
storeService.redisClient.Close()
|
||||
}
|
||||
|
||||
func SaveUrlMapping(shortUrl string, originUrl string, userId string) error {
|
||||
return storeService.redisClient.Set(ctx, shortUrl, originUrl, CacheDuration).Err()
|
||||
}
|
||||
|
||||
func RetrieveInitialUrl(shortUrl string) (string, error) {
|
||||
result, err := storeService.redisClient.Get(ctx, shortUrl).Result()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func DeleteShortUrl(shortUrl string) error {
|
||||
return storeService.redisClient.Set(ctx, shortUrl, "", time.Second).Err()
|
||||
}
|
||||
36
internal/shortener/shortener.go
Normal file
36
internal/shortener/shortener.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package shortener
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/itchyny/base58-go"
|
||||
)
|
||||
|
||||
func sha256Of(input string) []byte {
|
||||
algorithm := sha256.New()
|
||||
algorithm.Write([]byte(input))
|
||||
return algorithm.Sum(nil)
|
||||
}
|
||||
|
||||
func base58Encoded(bytes []byte) (string, error) {
|
||||
encoding := base58.BitcoinEncoding
|
||||
encoded, err := encoding.Encode(bytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(encoded), nil
|
||||
}
|
||||
|
||||
func GenerateShortLink(originUrl string, userId string) (string, error) {
|
||||
urlHashByte := sha256Of(originUrl + userId)
|
||||
generateNumber := new(big.Int).SetBytes(urlHashByte).Uint64()
|
||||
result, err := base58Encoded([]byte(fmt.Sprintf("%d", generateNumber)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return result[:8], nil
|
||||
}
|
||||
26
internal/shortener/shortener_test.go
Normal file
26
internal/shortener/shortener_test.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package shortener
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const userId = "7c729139-7ff4-445f-976b-2d842f55cb0e"
|
||||
|
||||
func TestGenerateShortLink(t *testing.T) {
|
||||
link1 := "https://www.baidu.com/"
|
||||
short1, err := GenerateShortLink(link1, userId)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, short1, "egtq236P")
|
||||
|
||||
link2 := "https://www.163.com/"
|
||||
short2, err := GenerateShortLink(link2, userId)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, short2, "DiCqg9Yp")
|
||||
|
||||
link3 := "https://www.qq.com/"
|
||||
short3, err := GenerateShortLink(link3, userId)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, short3, "4QhQ62cZ")
|
||||
}
|
||||
BIN
internal/templ/.DS_Store
vendored
Normal file
BIN
internal/templ/.DS_Store
vendored
Normal file
Binary file not shown.
67
internal/templ/auth/login.templ
Normal file
67
internal/templ/auth/login.templ
Normal file
@@ -0,0 +1,67 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/base"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/funcs"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/model"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
templ Login(r *http.Request, page string, form *model.LoginPageData) {
|
||||
@base.Base(page) {
|
||||
<div class="container">
|
||||
<div class="flex flex-column align-items row py-md-5 mt-md-5">
|
||||
<h1>登录</h1>
|
||||
<div class="col-sm-4 py-md-5">
|
||||
<form action="/login" method="post">
|
||||
@templ.Raw(funcs.CsrfField(r))
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">邮箱</span>
|
||||
</div>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
class="form-control"
|
||||
required
|
||||
id="email"
|
||||
value={ form.Email }
|
||||
aria-describedby="emailValid"
|
||||
/>
|
||||
</div>
|
||||
if form.EmailMsg != "" {
|
||||
<small id="emailValid" style="color: #f44336;" class="form-text">{ form.EmailMsg }</small>
|
||||
}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">密码</span>
|
||||
</div>
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
class="form-control"
|
||||
required
|
||||
id="password"
|
||||
value={ form.Password }
|
||||
aria-describedby="passwordValid"
|
||||
/>
|
||||
</div>
|
||||
if form.PasswordMsg!= "" {
|
||||
<small id="passwordValid" style="color: #f44336;" class="form-text">{ form.PasswordMsg }</small>
|
||||
}
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block">提交</button>
|
||||
</form>
|
||||
if form.Summary!= "" {
|
||||
<div class="py-md-5" style="color: #f44336;">
|
||||
{ form.Summary }
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
168
internal/templ/auth/login_templ.go
Normal file
168
internal/templ/auth/login_templ.go
Normal file
@@ -0,0 +1,168 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.793
|
||||
package auth
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/base"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/funcs"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/model"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Login(r *http.Request, page string, form *model.LoginPageData) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"container\"><div class=\"flex flex-column align-items row py-md-5 mt-md-5\"><h1>登录</h1><div class=\"col-sm-4 py-md-5\"><form action=\"/login\" method=\"post\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.Raw(funcs.CsrfField(r)).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"form-group\"><div class=\"input-group\"><div class=\"input-group-prepend\"><span class=\"input-group-text\">邮箱</span></div><input type=\"email\" name=\"email\" class=\"form-control\" required id=\"email\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(form.Email)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templ/auth/login.templ`, Line: 29, Col: 27}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" aria-describedby=\"emailValid\"></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if form.EmailMsg != "" {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<small id=\"emailValid\" style=\"color: #f44336;\" class=\"form-text\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(form.EmailMsg)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templ/auth/login.templ`, Line: 34, Col: 88}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</small>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"form-group\"><div class=\"input-group\"><div class=\"input-group-prepend\"><span class=\"input-group-text\">密码</span></div><input type=\"password\" name=\"password\" class=\"form-control\" required id=\"password\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(form.Password)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templ/auth/login.templ`, Line: 48, Col: 30}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" aria-describedby=\"passwordValid\"></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if form.PasswordMsg != "" {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<small id=\"passwordValid\" style=\"color: #f44336;\" class=\"form-text\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(form.PasswordMsg)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templ/auth/login.templ`, Line: 53, Col: 94}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</small>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><button type=\"submit\" class=\"btn btn-primary btn-block\">提交</button></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if form.Summary != "" {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"py-md-5\" style=\"color: #f44336;\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(form.Summary)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templ/auth/login.templ`, Line: 60, Col: 21}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
templ_7745c5c3_Err = base.Base(page).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
86
internal/templ/auth/register.templ
Normal file
86
internal/templ/auth/register.templ
Normal file
@@ -0,0 +1,86 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/base"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/funcs"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/model"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
templ Register(r *http.Request, page string, form *model.RegisterPageData) {
|
||||
@base.Base(page) {
|
||||
<div class="container">
|
||||
<div class="flex flex-column align-items row py-md-5 mt-md-5">
|
||||
<h1>注册</h1>
|
||||
<div class="col-sm-4 py-md-5">
|
||||
<form action="/register" method="post">
|
||||
@templ.Raw(funcs.CsrfField(r))
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">邮箱</span>
|
||||
</div>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
class="form-control"
|
||||
required
|
||||
id="email"
|
||||
value={ form.Email }
|
||||
aria-describedby="emailValid"
|
||||
/>
|
||||
</div>
|
||||
if form.Email!="" {
|
||||
<small id="emailValid" style="color: #f44336;" class="form-text">{ form.EmailMsg }</small>
|
||||
}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">名称</span>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
name="username"
|
||||
class="form-control"
|
||||
required
|
||||
id="username"
|
||||
value={ form.Username }
|
||||
aria-describedby="usernameValid"
|
||||
/>
|
||||
</div>
|
||||
if form.Username!="" {
|
||||
<small id="usernameValid" style="color: #f44336;" class="form-text">{ form.UsernameMsg }</small>
|
||||
}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">密码</span>
|
||||
</div>
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
class="form-control"
|
||||
required
|
||||
id="password"
|
||||
value={ form.Password }
|
||||
aria-describedby="passwordValid"
|
||||
/>
|
||||
</div>
|
||||
if form.Password!="" {
|
||||
<small id="passwordValid" style="color: #f44336;" class="form-text">{ form.PasswordMsg }</small>
|
||||
}
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block">提交</button>
|
||||
</form>
|
||||
if form.Summary != "" {
|
||||
<div class="py-md-5" style="color: #f44336;">
|
||||
{ form.Summary }
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
204
internal/templ/auth/register_templ.go
Normal file
204
internal/templ/auth/register_templ.go
Normal file
@@ -0,0 +1,204 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.793
|
||||
package auth
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/base"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/funcs"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/model"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Register(r *http.Request, page string, form *model.RegisterPageData) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"container\"><div class=\"flex flex-column align-items row py-md-5 mt-md-5\"><h1>注册</h1><div class=\"col-sm-4 py-md-5\"><form action=\"/register\" method=\"post\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.Raw(funcs.CsrfField(r)).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"form-group\"><div class=\"input-group\"><div class=\"input-group-prepend\"><span class=\"input-group-text\">邮箱</span></div><input type=\"email\" name=\"email\" class=\"form-control\" required id=\"email\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(form.Email)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templ/auth/register.templ`, Line: 29, Col: 27}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" aria-describedby=\"emailValid\"></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if form.Email != "" {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<small id=\"emailValid\" style=\"color: #f44336;\" class=\"form-text\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(form.EmailMsg)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templ/auth/register.templ`, Line: 34, Col: 88}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</small>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"form-group\"><div class=\"input-group\"><div class=\"input-group-prepend\"><span class=\"input-group-text\">名称</span></div><input type=\"text\" name=\"username\" class=\"form-control\" required id=\"username\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(form.Username)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templ/auth/register.templ`, Line: 48, Col: 30}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" aria-describedby=\"usernameValid\"></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if form.Username != "" {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<small id=\"usernameValid\" style=\"color: #f44336;\" class=\"form-text\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(form.UsernameMsg)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templ/auth/register.templ`, Line: 53, Col: 94}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</small>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"form-group\"><div class=\"input-group\"><div class=\"input-group-prepend\"><span class=\"input-group-text\">密码</span></div><input type=\"password\" name=\"password\" class=\"form-control\" required id=\"password\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(form.Password)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templ/auth/register.templ`, Line: 67, Col: 30}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" aria-describedby=\"passwordValid\"></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if form.Password != "" {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<small id=\"passwordValid\" style=\"color: #f44336;\" class=\"form-text\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(form.PasswordMsg)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templ/auth/register.templ`, Line: 72, Col: 94}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</small>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><button type=\"submit\" class=\"btn btn-primary btn-block\">提交</button></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if form.Summary != "" {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"py-md-5\" style=\"color: #f44336;\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(form.Summary)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templ/auth/register.templ`, Line: 79, Col: 21}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
templ_7745c5c3_Err = base.Base(page).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
59
internal/templ/base/base.templ
Normal file
59
internal/templ/base/base.templ
Normal file
@@ -0,0 +1,59 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/css"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/funcs"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/js"
|
||||
)
|
||||
|
||||
templ Base(page string) {
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
|
||||
<link rel="shortcut icon" href="/assets/favicon.ico" type="image/x-icon"/>
|
||||
<link rel="stylesheet" href="/assets/css/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" href="/assets/css/index.css"/>
|
||||
switch page {
|
||||
case "home":
|
||||
@css.HomeCSS()
|
||||
}
|
||||
<title>URL段地址服务</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<nav class="navbar navbar-light bg-light navbar-wh">
|
||||
<a class="navbar-brand navbar-brand-fs" href="/">
|
||||
URL段地址服务
|
||||
</a>
|
||||
<ul class="flex oauth">
|
||||
{{ auth := funcs.GetAuthorize(ctx) }}
|
||||
if auth != nil {
|
||||
<li style="font-size: 12px;">
|
||||
欢迎您: { auth.Name }
|
||||
</li>
|
||||
<li style="font-size: 12px;">
|
||||
<a href="/logout" class="btn btn-primary btn-sm">退出</a>
|
||||
</li>
|
||||
} else {
|
||||
<li>
|
||||
<a href="/login" class="btn btn-outline-primary btn-sm">登录</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/register" class="btn btn-primary btn-sm">注册</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
{ children... }
|
||||
<script src="/assets/js/jquery.min.js"></script>
|
||||
<script src="/assets/js/bootstrap.bundle.min.js"></script>
|
||||
switch page {
|
||||
case "home":
|
||||
@js.HomeJS()
|
||||
}
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
105
internal/templ/base/base_templ.go
Normal file
105
internal/templ/base/base_templ.go
Normal file
@@ -0,0 +1,105 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.793
|
||||
package base
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/css"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/funcs"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/js"
|
||||
)
|
||||
|
||||
func Base(page string) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html><html lang=\"zh-CN\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\"><link rel=\"shortcut icon\" href=\"/assets/favicon.ico\" type=\"image/x-icon\"><link rel=\"stylesheet\" href=\"/assets/css/bootstrap.min.css\"><link rel=\"stylesheet\" href=\"/assets/css/index.css\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
switch page {
|
||||
case "home":
|
||||
templ_7745c5c3_Err = css.HomeCSS().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<title>URL段地址服务</title></head><body><div class=\"wrapper\"><nav class=\"navbar navbar-light bg-light navbar-wh\"><a class=\"navbar-brand navbar-brand-fs\" href=\"/\">URL段地址服务</a><ul class=\"flex oauth\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
auth := funcs.GetAuthorize(ctx)
|
||||
if auth != nil {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li style=\"font-size: 12px;\">欢迎您: ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(auth.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templ/base/base.templ`, Line: 34, Col: 30}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</li><li style=\"font-size: 12px;\"><a href=\"/logout\" class=\"btn btn-primary btn-sm\">退出</a></li>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li><a href=\"/login\" class=\"btn btn-outline-primary btn-sm\">登录</a></li><li><a href=\"/register\" class=\"btn btn-primary btn-sm\">注册</a></li>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</ul></nav></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<script src=\"/assets/js/jquery.min.js\"></script><script src=\"/assets/js/bootstrap.bundle.min.js\"></script>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
switch page {
|
||||
case "home":
|
||||
templ_7745c5c3_Err = js.HomeJS().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</body></html>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
23
internal/templ/css/home.templ
Normal file
23
internal/templ/css/home.templ
Normal file
@@ -0,0 +1,23 @@
|
||||
package css
|
||||
|
||||
templ HomeCSS() {
|
||||
<style>
|
||||
.my_table {
|
||||
display: block;
|
||||
max-width: 1280px;
|
||||
}
|
||||
|
||||
.my_table tr {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
border: 1px solid #eee;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.my_table tr td {
|
||||
display: inline-block;
|
||||
word-wrap: break-word;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
</style>
|
||||
}
|
||||
40
internal/templ/css/home_templ.go
Normal file
40
internal/templ/css/home_templ.go
Normal file
@@ -0,0 +1,40 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.793
|
||||
package css
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
func HomeCSS() templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<style>\n .my_table {\n display: block;\n max-width: 1280px;\n }\n\n .my_table tr {\n display: inline-block;\n width: 100%;\n border: 1px solid #eee;\n border-collapse: collapse;\n }\n\n .my_table tr td {\n display: inline-block;\n word-wrap: break-word;\n padding: 2px 5px;\n }\n</style>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
10
internal/templ/err/404.templ
Normal file
10
internal/templ/err/404.templ
Normal file
@@ -0,0 +1,10 @@
|
||||
package err
|
||||
|
||||
import "github.com/zhang2092/go-url-shortener/internal/templ/base"
|
||||
|
||||
templ Error404() {
|
||||
@base.Base("404") {
|
||||
<h1>404</h1>
|
||||
<p>当前短路径已经失效</p>
|
||||
}
|
||||
}
|
||||
60
internal/templ/err/404_templ.go
Normal file
60
internal/templ/err/404_templ.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.793
|
||||
package err
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "github.com/zhang2092/go-url-shortener/internal/templ/base"
|
||||
|
||||
func Error404() templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h1>404</h1><p>当前短路径已经失效</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
templ_7745c5c3_Err = base.Base("404").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
11
internal/templ/funcs/authorize.go
Normal file
11
internal/templ/funcs/authorize.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package funcs
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zhang2092/go-url-shortener/internal/middleware"
|
||||
)
|
||||
|
||||
func GetAuthorize(ctx context.Context) *middleware.Authorize {
|
||||
return middleware.GetUser(ctx)
|
||||
}
|
||||
12
internal/templ/funcs/csrf.go
Normal file
12
internal/templ/funcs/csrf.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package funcs
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/csrf"
|
||||
)
|
||||
|
||||
func CsrfField(r *http.Request) template.HTML {
|
||||
return csrf.TemplateField(r)
|
||||
}
|
||||
51
internal/templ/home/home.templ
Normal file
51
internal/templ/home/home.templ
Normal file
@@ -0,0 +1,51 @@
|
||||
package home
|
||||
|
||||
import (
|
||||
"github.com/zhang2092/go-url-shortener/internal/db"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/base"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/funcs"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
templ Home(r *http.Request, page string, data []*db.UserRelateUrl) {
|
||||
@base.Base(page) {
|
||||
<div class="container-fluid flex justify-content">
|
||||
<div class="main">
|
||||
<h3 style="margin-top: 20px;margin-bottom: 10px;">
|
||||
短地址列表
|
||||
<a
|
||||
class="btn btn-primary"
|
||||
href="/create-short-url"
|
||||
>添加</a>
|
||||
</h3>
|
||||
<table class="my_table" style="display: block;">
|
||||
<tr>
|
||||
<th width="600px">原地址</th>
|
||||
<th width="320px">短地址</th>
|
||||
<th width="80px">是否有效</th>
|
||||
<th width="80px">删除</th>
|
||||
</tr>
|
||||
for _, item := range data {
|
||||
<tr>
|
||||
<td width="600px">{ item.OriginUrl }</td>
|
||||
<td width="320px"><a target="_blank" href={ templ.URL(item.ShortUrl) }>{ item.ShortUrl }</a></td>
|
||||
<td width="80px">
|
||||
if item.Status == 0 {
|
||||
<code>YES</code>
|
||||
} else {
|
||||
<code>NO</code>
|
||||
}
|
||||
</td>
|
||||
<td width="80px">
|
||||
if item.Status == 0 {
|
||||
<button data-short-url={ item.ShortUrl } class="btn btn-danger deleteShortUrl">删除</button>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
@templ.Raw(funcs.CsrfField(r))
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
156
internal/templ/home/home_templ.go
Normal file
156
internal/templ/home/home_templ.go
Normal file
@@ -0,0 +1,156 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.793
|
||||
package home
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"github.com/zhang2092/go-url-shortener/internal/db"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/base"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/funcs"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Home(r *http.Request, page string, data []*db.UserRelateUrl) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"container-fluid flex justify-content\"><div class=\"main\"><h3 style=\"margin-top: 20px;margin-bottom: 10px;\">短地址列表 <a class=\"btn btn-primary\" href=\"/create-short-url\">添加</a></h3><table class=\"my_table\" style=\"display: block;\"><tr><th width=\"600px\">原地址</th><th width=\"320px\">短地址</th><th width=\"80px\">是否有效</th><th width=\"80px\">删除</th></tr>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, item := range data {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<tr><td width=\"600px\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(item.OriginUrl)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templ/home/home.templ`, Line: 30, Col: 41}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td width=\"320px\"><a target=\"_blank\" href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 templ.SafeURL = templ.URL(item.ShortUrl)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var4)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(item.ShortUrl)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templ/home/home.templ`, Line: 31, Col: 93}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></td><td width=\"80px\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if item.Status == 0 {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<code>YES</code>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<code>NO</code>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td width=\"80px\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if item.Status == 0 {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<button data-short-url=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(item.ShortUrl)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templ/home/home.templ`, Line: 41, Col: 47}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" class=\"btn btn-danger deleteShortUrl\">删除</button>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td></tr>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</table>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.Raw(funcs.CsrfField(r)).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
templ_7745c5c3_Err = base.Base(page).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
28
internal/templ/js/home.templ
Normal file
28
internal/templ/js/home.templ
Normal file
@@ -0,0 +1,28 @@
|
||||
package js
|
||||
|
||||
templ HomeJS() {
|
||||
<script type="text/javascript">
|
||||
$('.deleteShortUrl').click(function () {
|
||||
let csrfToken = $('input[name="csrf_token"]').val()
|
||||
let u = $(this).attr('data-short-url')
|
||||
$.ajax({
|
||||
url: '/delete-short-url/' + u,
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
headers: {
|
||||
"X-CSRF-Token": csrfToken
|
||||
},
|
||||
success: function (res) {
|
||||
if (res.success) {
|
||||
alert('删除成功');
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert('删除失败');
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
}
|
||||
40
internal/templ/js/home_templ.go
Normal file
40
internal/templ/js/home_templ.go
Normal file
@@ -0,0 +1,40 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.793
|
||||
package js
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
func HomeJS() templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<script type=\"text/javascript\">\n $('.deleteShortUrl').click(function () {\n let csrfToken = $('input[name=\"csrf_token\"]').val()\n let u = $(this).attr('data-short-url')\n $.ajax({\n url: '/delete-short-url/' + u,\n type: 'POST',\n cache: false,\n processData: false,\n contentType: false,\n headers: {\n \"X-CSRF-Token\": csrfToken\n },\n success: function (res) {\n if (res.success) {\n alert('删除成功');\n window.location.reload();\n } else {\n alert('删除失败');\n }\n }\n })\n });\n</script>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
19
internal/templ/model/auth.go
Normal file
19
internal/templ/model/auth.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package model
|
||||
|
||||
type LoginPageData struct {
|
||||
Summary string
|
||||
Email string
|
||||
EmailMsg string
|
||||
Password string
|
||||
PasswordMsg string
|
||||
}
|
||||
|
||||
type RegisterPageData struct {
|
||||
Summary string
|
||||
Email string
|
||||
EmailMsg string
|
||||
Username string
|
||||
UsernameMsg string
|
||||
Password string
|
||||
PasswordMsg string
|
||||
}
|
||||
38
internal/templ/templ.go
Normal file
38
internal/templ/templ.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package templ
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/zhang2092/go-url-shortener/internal/db"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/auth"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/err"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/home"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/model"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/url"
|
||||
)
|
||||
|
||||
func Login(w http.ResponseWriter, r *http.Request, form *model.LoginPageData) {
|
||||
checkErr(w, auth.Login(r, "login", form).Render(r.Context(), w))
|
||||
}
|
||||
|
||||
func Register(w http.ResponseWriter, r *http.Request, form *model.RegisterPageData) {
|
||||
checkErr(w, auth.Register(r, "register", form).Render(r.Context(), w))
|
||||
}
|
||||
|
||||
func Home(w http.ResponseWriter, r *http.Request, data []*db.UserRelateUrl) {
|
||||
checkErr(w, home.Home(r, "home", data).Render(r.Context(), w))
|
||||
}
|
||||
|
||||
func CreateUrl(w http.ResponseWriter, r *http.Request, errorMsg string) {
|
||||
checkErr(w, url.CreateUrl(r, "create_url", errorMsg).Render(r.Context(), w))
|
||||
}
|
||||
|
||||
func Error404(w http.ResponseWriter, r *http.Request) {
|
||||
checkErr(w, err.Error404().Render(r.Context(), w))
|
||||
}
|
||||
|
||||
func checkErr(w http.ResponseWriter, err error) {
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
34
internal/templ/url/url.templ
Normal file
34
internal/templ/url/url.templ
Normal file
@@ -0,0 +1,34 @@
|
||||
package url
|
||||
|
||||
import (
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/base"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/funcs"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
templ CreateUrl(r *http.Request, page string, errorMsg string) {
|
||||
@base.Base(page) {
|
||||
<div class="container">
|
||||
<div class="flex flex-column align-items row py-md-5 mt-md-5">
|
||||
<h1>创建短路径</h1>
|
||||
<div class="col-sm-4 py-md-5">
|
||||
<form action="/create-short-url" method="post">
|
||||
@templ.Raw(funcs.CsrfField(r))
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">原路径</span>
|
||||
</div>
|
||||
<input type="text" name="long_url" class="form-control" required id="long_url"/>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block">创建</button>
|
||||
</form>
|
||||
if errorMsg != "" {
|
||||
<div class="py-md-5" style="color: #f44336;"></div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
82
internal/templ/url/url_templ.go
Normal file
82
internal/templ/url/url_templ.go
Normal file
@@ -0,0 +1,82 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.793
|
||||
package url
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/base"
|
||||
"github.com/zhang2092/go-url-shortener/internal/templ/funcs"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func CreateUrl(r *http.Request, page string, errorMsg string) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"container\"><div class=\"flex flex-column align-items row py-md-5 mt-md-5\"><h1>创建短路径</h1><div class=\"col-sm-4 py-md-5\"><form action=\"/create-short-url\" method=\"post\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.Raw(funcs.CsrfField(r)).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"form-group\"><div class=\"input-group\"><div class=\"input-group-prepend\"><span class=\"input-group-text\">原路径</span></div><input type=\"text\" name=\"long_url\" class=\"form-control\" required id=\"long_url\"></div></div><button type=\"submit\" class=\"btn btn-primary btn-block\">创建</button></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if errorMsg != "" {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"py-md-5\" style=\"color: #f44336;\"></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
templ_7745c5c3_Err = base.Base(page).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
Reference in New Issue
Block a user