add models for MailAccount and Mail
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
This commit is contained in:
@@ -67,6 +67,8 @@ var exportCmd = &cobra.Command{
|
|||||||
Permission []models.PermissionDTO `json:"permission"`
|
Permission []models.PermissionDTO `json:"permission"`
|
||||||
Assignment []models.AssignmentDTO `json:"assignment"`
|
Assignment []models.AssignmentDTO `json:"assignment"`
|
||||||
Token []models.TokenDTO `json:"token"`
|
Token []models.TokenDTO `json:"token"`
|
||||||
|
MailAccount []map[string]interface{} `json:"mail_account"`
|
||||||
|
Mail []map[string]interface{} `json:"mail"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -97,12 +99,15 @@ var exportCmd = &cobra.Command{
|
|||||||
mediaarticles []models.MediaArticleDTO
|
mediaarticles []models.MediaArticleDTO
|
||||||
mediavideos []models.MediaVideoDTO
|
mediavideos []models.MediaVideoDTO
|
||||||
mediaactors []models.MediaActorDTO
|
mediaactors []models.MediaActorDTO
|
||||||
|
mediaactorfiles []models.MediaActorFileDTO
|
||||||
mediafiles []models.MediaFileDTO
|
mediafiles []models.MediaFileDTO
|
||||||
medialofis []models.MediaLoFiDTO
|
medialofis []models.MediaLoFiDTO
|
||||||
profiles []models.ProfileDTO
|
profiles []models.ProfileDTO
|
||||||
permissions []models.PermissionDTO
|
permissions []models.PermissionDTO
|
||||||
assignments []models.AssignmentDTO
|
assignments []models.AssignmentDTO
|
||||||
tokens []models.TokenDTO
|
tokens []models.TokenDTO
|
||||||
|
mailaccounts []map[string]interface{}
|
||||||
|
mail []map[string]interface{}
|
||||||
)
|
)
|
||||||
|
|
||||||
registry := map[string]any{
|
registry := map[string]any{
|
||||||
@@ -134,11 +139,14 @@ var exportCmd = &cobra.Command{
|
|||||||
"media/videos": &mediavideos,
|
"media/videos": &mediavideos,
|
||||||
"media/actors": &mediaactors,
|
"media/actors": &mediaactors,
|
||||||
"media/files": &mediafiles,
|
"media/files": &mediafiles,
|
||||||
|
"media/actorfiles": &mediaactorfiles,
|
||||||
"media/lofis": &medialofis,
|
"media/lofis": &medialofis,
|
||||||
"user/profiles": &profiles,
|
"user/profiles": &profiles,
|
||||||
"user/permissions": &permissions,
|
"user/permissions": &permissions,
|
||||||
"user/assignments": &assignments,
|
"user/assignments": &assignments,
|
||||||
"user/tokens": &tokens,
|
"user/tokens": &tokens,
|
||||||
|
"admin/mailaccounts": &mailaccounts,
|
||||||
|
"user/mail": &mail,
|
||||||
}
|
}
|
||||||
for path, value := range registry {
|
for path, value := range registry {
|
||||||
GetJSON(client, login.Token, fmt.Sprintf("%s/%s/%s", server, "api/v1", path), value)
|
GetJSON(client, login.Token, fmt.Sprintf("%s/%s/%s", server, "api/v1", path), value)
|
||||||
@@ -177,6 +185,8 @@ var exportCmd = &cobra.Command{
|
|||||||
Permission: permissions,
|
Permission: permissions,
|
||||||
Assignment: assignments,
|
Assignment: assignments,
|
||||||
Token: tokens,
|
Token: tokens,
|
||||||
|
MailAccount: mailaccounts,
|
||||||
|
Mail: mail,
|
||||||
}
|
}
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
jsonData, err := json.MarshalIndent(export, "", " ")
|
jsonData, err := json.MarshalIndent(export, "", " ")
|
||||||
|
|||||||
@@ -37,6 +37,6 @@ func Execute() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.myapp.yaml)")
|
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.config/kli.yaml)")
|
||||||
rootCmd.PersistentFlags().CountVarP(&verbose, "verbose", "v", "increase verbosity (-v, -vv, -vvv)")
|
rootCmd.PersistentFlags().CountVarP(&verbose, "verbose", "v", "increase verbosity (-v, -vv, -vvv)")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,22 +8,26 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type AdminHandler struct {
|
type AdminHandler struct {
|
||||||
AuthHandler *AuthHandler
|
AuthHandler *AuthHandler
|
||||||
ProfileHandler *ProfileHandler
|
ProfileHandler *ProfileHandler
|
||||||
PermissionHandler *PermissionHandler
|
PermissionHandler *PermissionHandler
|
||||||
AssignmentHandler *AssignmentHandler
|
AssignmentHandler *AssignmentHandler
|
||||||
TokenHandler *TokenHandler
|
TokenHandler *TokenHandler
|
||||||
Secret []byte
|
MailAccountHandler *MailAccountHandler
|
||||||
|
MailHandler *MailHandler
|
||||||
|
Secret []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAdminHandler(db *gorm.DB, secret []byte) *AdminHandler {
|
func NewAdminHandler(db *gorm.DB, secret []byte) *AdminHandler {
|
||||||
adminHandler := &AdminHandler{
|
adminHandler := &AdminHandler{
|
||||||
AuthHandler: &AuthHandler{Service: service.NewAuthService(repository.NewProfileRepository(db), secret)},
|
AuthHandler: &AuthHandler{Service: service.NewAuthService(repository.NewProfileRepository(db), secret)},
|
||||||
ProfileHandler: NewProfileHandler(db),
|
ProfileHandler: NewProfileHandler(db),
|
||||||
PermissionHandler: &PermissionHandler{Service: service.NewPermissionService(repository.NewPermissionRepository(db))},
|
PermissionHandler: &PermissionHandler{Service: service.NewPermissionService(repository.NewPermissionRepository(db))},
|
||||||
AssignmentHandler: NewAssignmentHandler(db),
|
AssignmentHandler: NewAssignmentHandler(db),
|
||||||
TokenHandler: NewTokenHandler(db),
|
TokenHandler: NewTokenHandler(db),
|
||||||
Secret: secret,
|
MailAccountHandler: NewMailAccountHandler(db),
|
||||||
|
MailHandler: NewMailHandler(db),
|
||||||
|
Secret: secret,
|
||||||
}
|
}
|
||||||
return adminHandler
|
return adminHandler
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/internal/models"
|
||||||
|
"kontor-api-gin/internal/repository"
|
||||||
|
"kontor-api-gin/internal/service"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MailHandler struct {
|
||||||
|
Service *service.MailService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMailHandler(db *gorm.DB) *MailHandler {
|
||||||
|
handler := &MailHandler{Service: service.NewMailService(repository.NewMailRepository(db))}
|
||||||
|
return handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *MailHandler) List(c *gin.Context) {
|
||||||
|
mails, err := handler.Service.List(c.Request.Context())
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dtos := models.ConvertMailsToDtos(mails)
|
||||||
|
c.JSON(http.StatusOK, dtos)
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/internal/models"
|
||||||
|
"kontor-api-gin/internal/repository"
|
||||||
|
"kontor-api-gin/internal/service"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MailAccountHandler struct {
|
||||||
|
Service *service.MailAccountService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMailAccountHandler(db *gorm.DB) *MailAccountHandler {
|
||||||
|
handler := &MailAccountHandler{Service: service.NewMailAccountService(repository.NewMailAccountRepository(db))}
|
||||||
|
return handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *MailAccountHandler) List(c *gin.Context) {
|
||||||
|
mailaccounts, err := handler.Service.List(c.Request.Context())
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dtos := models.ConvertMailAccountsToDtos(mailaccounts)
|
||||||
|
c.JSON(http.StatusOK, dtos)
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/internal/helpers"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Mail struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;" json:"id"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_date" json:"created_date"`
|
||||||
|
UpdatedAt time.Time `gorm:"column:last_modified_date" json:"last_modified_date"`
|
||||||
|
DeletedAt *time.Time `gorm:"column:deleted_date" json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
Folder string `gorm:"column:folder" json:"folder"`
|
||||||
|
Subject string `gorm:"column:subject" json:"subject"`
|
||||||
|
Body string `gorm:"column:body" json:"body"`
|
||||||
|
SendAt time.Time `gorm:"column:sent_date" json:"sent_date"`
|
||||||
|
ReceivedAt time.Time `gorm:"column:received_date" json:"received_date"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mail *Mail) TableName() string {
|
||||||
|
return "mail"
|
||||||
|
}
|
||||||
|
|
||||||
|
type MailDTO struct {
|
||||||
|
ID string `json:"id" binding:"required"`
|
||||||
|
CreatedAt helpers.KontorTime `json:"created_date"`
|
||||||
|
UpdatedAt helpers.KontorTime `json:"last_modified_date"`
|
||||||
|
DeletedAt *time.Time `json:"deleted_date.omitempty"`
|
||||||
|
Version int `json:"version"`
|
||||||
|
Folder string `json:"folder"`
|
||||||
|
Subject string `json:"subject"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
SendAt time.Time `json:"sent_date"`
|
||||||
|
ReceivedAt time.Time `json:"received_date"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertMailsToDtos(mails []Mail) []MailDTO {
|
||||||
|
dtos := make([]MailDTO, len(mails))
|
||||||
|
for i, mail := range mails {
|
||||||
|
dtos[i] = MailDTO{
|
||||||
|
ID: mail.ID,
|
||||||
|
CreatedAt: helpers.KontorTime{Time: mail.CreatedAt},
|
||||||
|
UpdatedAt: helpers.KontorTime{Time: mail.UpdatedAt},
|
||||||
|
DeletedAt: mail.DeletedAt,
|
||||||
|
Version: mail.Version,
|
||||||
|
Folder: mail.Folder,
|
||||||
|
Subject: mail.Subject,
|
||||||
|
Body: mail.Body,
|
||||||
|
SendAt: mail.SendAt,
|
||||||
|
ReceivedAt: mail.ReceivedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dtos
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/internal/helpers"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MailAccount struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;" json:"id"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_date" json:"created_date"`
|
||||||
|
UpdatedAt time.Time `gorm:"column:last_modified_date" json:"last_modified_date"`
|
||||||
|
DeletedAt *time.Time `gorm:"column:deleted_date" json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
Host string `gorm:"column:host" json:"host"`
|
||||||
|
Port int `gorm:"column:port" json:"port"`
|
||||||
|
Protocol string `gorm:"column:protocol" json:"protocol"`
|
||||||
|
UserName string `gorm:"column:user_name" json:"user_name"`
|
||||||
|
Password string `gorm:"column:password" json:"password"`
|
||||||
|
StartTLS bool `gorm:"column:start_tls" json:"start_tls"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mail_account *MailAccount) TableName() string {
|
||||||
|
return "mail_account"
|
||||||
|
}
|
||||||
|
|
||||||
|
type MailAccountDTO struct {
|
||||||
|
ID string `json:"id" binding:"required"`
|
||||||
|
CreatedAt helpers.KontorTime `json:"created_date"`
|
||||||
|
UpdatedAt helpers.KontorTime `json:"last_modified_date"`
|
||||||
|
DeletedAt *time.Time `json:"deleted_date.omitempty"`
|
||||||
|
Version int `json:"version"`
|
||||||
|
Host string `json:"host"`
|
||||||
|
Port int `json:"port"`
|
||||||
|
Protocol string `json:"protocol"`
|
||||||
|
UserName string `json:"user_name"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
StartTLS bool `json:"start_tls"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertMailAccountsToDtos(mailaccounts []MailAccount) []MailAccountDTO {
|
||||||
|
dtos := make([]MailAccountDTO, len(mailaccounts))
|
||||||
|
for i, mailaccount := range mailaccounts {
|
||||||
|
dtos[i] = MailAccountDTO{
|
||||||
|
ID: mailaccount.ID,
|
||||||
|
CreatedAt: helpers.KontorTime{Time: mailaccount.CreatedAt},
|
||||||
|
UpdatedAt: helpers.KontorTime{Time: mailaccount.UpdatedAt},
|
||||||
|
DeletedAt: mailaccount.DeletedAt,
|
||||||
|
Version: mailaccount.Version,
|
||||||
|
Host: mailaccount.Host,
|
||||||
|
Port: mailaccount.Port,
|
||||||
|
Protocol: mailaccount.Protocol,
|
||||||
|
UserName: mailaccount.UserName,
|
||||||
|
Password: mailaccount.Password,
|
||||||
|
StartTLS: mailaccount.StartTLS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dtos
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"kontor-api-gin/internal/models"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MailRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMailRepository(db *gorm.DB) *MailRepository {
|
||||||
|
repository := &MailRepository{db: db}
|
||||||
|
return repository
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repository *MailRepository) FindAll(context context.Context) ([]models.Mail, error) {
|
||||||
|
var mails []models.Mail
|
||||||
|
|
||||||
|
result := repository.db.Find(&mails)
|
||||||
|
slog.Info("MailRepository#FindAll", "count", result.RowsAffected)
|
||||||
|
|
||||||
|
return mails, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repository *MailRepository) GetById(context context.Context, id string) (models.Mail, error) {
|
||||||
|
var mail = models.Mail{ID: id}
|
||||||
|
slog.Info("MailRepository#GetById", "id", id)
|
||||||
|
result := repository.db.First(&mail)
|
||||||
|
if result.Error != nil {
|
||||||
|
slog.Info("MailRepository#GetById", "error", result.Error.Error())
|
||||||
|
}
|
||||||
|
return mail, result.Error
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"kontor-api-gin/internal/models"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MailAccountRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMailAccountRepository(db *gorm.DB) *MailAccountRepository {
|
||||||
|
repository := &MailAccountRepository{db: db}
|
||||||
|
return repository
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repository *MailAccountRepository) FindAll(context context.Context) ([]models.MailAccount, error) {
|
||||||
|
var mailaccounts []models.MailAccount
|
||||||
|
|
||||||
|
result := repository.db.Find(&mailaccounts)
|
||||||
|
slog.Info("MailAccountRepository#FindAll", "count", result.RowsAffected)
|
||||||
|
|
||||||
|
return mailaccounts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repository *MailAccountRepository) GetById(context context.Context, id string) (models.MailAccount, error) {
|
||||||
|
var mailaccount = models.MailAccount{ID: id}
|
||||||
|
slog.Info("MailAccountRepository#GetById", "id", id)
|
||||||
|
result := repository.db.First(&mailaccount)
|
||||||
|
if result.Error != nil {
|
||||||
|
slog.Info("MailAccountRepository#GetById", "error", result.Error.Error())
|
||||||
|
}
|
||||||
|
return mailaccount, result.Error
|
||||||
|
}
|
||||||
@@ -70,9 +70,11 @@ func New(admin *handlers.AdminHandler, bookshelf *handlers.BookshelfHandler, com
|
|||||||
{
|
{
|
||||||
secured := v1.Group("")
|
secured := v1.Group("")
|
||||||
secured.Use(middleware.JWT(admin.Secret))
|
secured.Use(middleware.JWT(admin.Secret))
|
||||||
|
secured.GET("admin/mailaccounts", admin.MailAccountHandler.List)
|
||||||
secured.GET("user/profiles", admin.ProfileHandler.List)
|
secured.GET("user/profiles", admin.ProfileHandler.List)
|
||||||
secured.GET("user/permissions", admin.PermissionHandler.List)
|
secured.GET("user/permissions", admin.PermissionHandler.List)
|
||||||
secured.GET("user/assignments", admin.AssignmentHandler.List)
|
secured.GET("user/assignments", admin.AssignmentHandler.List)
|
||||||
|
secured.GET("user/mails", admin.MailHandler.List)
|
||||||
secured.GET("user/tokens", admin.TokenHandler.List)
|
secured.GET("user/tokens", admin.TokenHandler.List)
|
||||||
secured.GET("tysc/sports", tysc.SportHandler.List)
|
secured.GET("tysc/sports", tysc.SportHandler.List)
|
||||||
secured.GET("tysc/sports/:id", tysc.SportHandler.Get)
|
secured.GET("tysc/sports/:id", tysc.SportHandler.Get)
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"kontor-api-gin/internal/models"
|
||||||
|
"kontor-api-gin/internal/repository"
|
||||||
|
"log/slog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MailService struct {
|
||||||
|
repository *repository.MailRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMailService(repo *repository.MailRepository) *MailService {
|
||||||
|
service := &MailService{repository: repo}
|
||||||
|
return service
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *MailService) List(context context.Context) ([]models.Mail, error) {
|
||||||
|
slog.Info("MailService#List")
|
||||||
|
return service.repository.FindAll(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *MailService) Get(context context.Context, id string) (models.Mail, error) {
|
||||||
|
slog.Info("MailService#Get")
|
||||||
|
return service.repository.GetById(context, id)
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"kontor-api-gin/internal/models"
|
||||||
|
"kontor-api-gin/internal/repository"
|
||||||
|
"log/slog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MailAccountService struct {
|
||||||
|
repository *repository.MailAccountRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMailAccountService(repo *repository.MailAccountRepository) *MailAccountService {
|
||||||
|
service := &MailAccountService{repository: repo}
|
||||||
|
return service
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *MailAccountService) List(context context.Context) ([]models.MailAccount, error) {
|
||||||
|
slog.Info("MailAccountService#List")
|
||||||
|
return service.repository.FindAll(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *MailAccountService) Get(context context.Context, id string) (models.MailAccount, error) {
|
||||||
|
slog.Info("MailAccountService#Get")
|
||||||
|
return service.repository.GetById(context, id)
|
||||||
|
}
|
||||||
|
|
||||||
@@ -17,9 +17,10 @@
|
|||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link dropdown-toggle" href="#" id="navbarWebMenuLink" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Media</a>
|
<a class="nav-link dropdown-toggle" href="#" id="navbarWebMenuLink" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Media</a>
|
||||||
<div class="dropdown-menu" aria-labelledby="navbarWebMenuLink">
|
<div class="dropdown-menu" aria-labelledby="navbarWebMenuLink">
|
||||||
<a class="dropdown-item" href="/web/files">Media Files</a>
|
<a class="dropdown-item" href="/web/media-files">Media Files</a>
|
||||||
<a class="dropdown-item" href="/web/actors">Media Actors</a>
|
<a class="dropdown-item" href="/web/actors">Media Actors</a>
|
||||||
<a class="dropdown-item" href="/web/lofis">Lofi</a>
|
<a class="dropdown-item" href="/web/media-lofis">Lofi</a>
|
||||||
|
<a class="dropdown-item" href="/web/articles">Artikel</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
{{ if .user }}
|
{{ if .user }}
|
||||||
|
|||||||
Reference in New Issue
Block a user