diff --git a/backend/cmd/serve.go b/backend/cmd/serve.go index 86f2bf4..1629c98 100644 --- a/backend/cmd/serve.go +++ b/backend/cmd/serve.go @@ -8,9 +8,11 @@ import ( "trankilou.fr/lassistanoque/backend/internal/adapter/auth/password" "trankilou.fr/lassistanoque/backend/internal/adapter/database" "trankilou.fr/lassistanoque/backend/internal/adapter/file" + "trankilou.fr/lassistanoque/backend/internal/adapter/llm" "trankilou.fr/lassistanoque/backend/internal/adapter/security" "trankilou.fr/lassistanoque/backend/internal/http" "trankilou.fr/lassistanoque/backend/internal/service/auth" + "trankilou.fr/lassistanoque/backend/internal/service/provider" "trankilou.fr/lassistanoque/backend/internal/service/storage" "trankilou.fr/lassistanoque/backend/internal/service/user" ) @@ -56,6 +58,7 @@ func runServe() { // Adapters tokenManager := security.NewJwtTokenManager(12*time.Hour, 7*24*time.Hour, "lassistanoque") pwdAuth := password.NewPasswordAuthenticator(tokenManager, db.UserRepository()) + llmEngine := llm.NewAnyLLMEngine() // services authService := auth.NewService( @@ -67,13 +70,15 @@ func runServe() { ) userService := user.NewService(db.UserRepository()) storageService := storage.NewService(store) + providerService := provider.NewService(db.ProviderRepository(), db.UserRepository(), llmEngine) // http server router := http.NewRouter(http.Dependencies{ - StorageService: storageService, - AuthService: authService, - UserService: userService, - TokenManager: tokenManager, + StorageService: storageService, + AuthService: authService, + UserService: userService, + TokenManager: tokenManager, + ProviderService: providerService, }) if err := router.Start(); err != nil { diff --git a/backend/internal/adapter/database/factory.go b/backend/internal/adapter/database/factory.go index d3253e7..4a8a48c 100644 --- a/backend/internal/adapter/database/factory.go +++ b/backend/internal/adapter/database/factory.go @@ -12,6 +12,7 @@ type Database interface { SettingsRepository() domain.SettingsRepository UserRepository() domain.UserRepository FileRepository() domain.FileRepository + ProviderRepository() domain.ProviderRepository Migrate() error Close() } diff --git a/backend/internal/adapter/database/turso/db.go b/backend/internal/adapter/database/turso/db.go index 4d6303e..5ed7968 100644 --- a/backend/internal/adapter/database/turso/db.go +++ b/backend/internal/adapter/database/turso/db.go @@ -67,3 +67,7 @@ func (db *TursoDB) SettingsRepository() domain.SettingsRepository { func (db *TursoDB) FileRepository() domain.FileRepository { return NewTursoFileRepository(db.DB) } + +func (db *TursoDB) ProviderRepository() domain.ProviderRepository { + return NewTursoModelRepository(db.DB) +} diff --git a/backend/internal/adapter/database/turso/migrations/000001_create_tables.down.sql b/backend/internal/adapter/database/turso/migrations/000001_create_tables.down.sql index 6c17ca8..b62c63c 100644 --- a/backend/internal/adapter/database/turso/migrations/000001_create_tables.down.sql +++ b/backend/internal/adapter/database/turso/migrations/000001_create_tables.down.sql @@ -11,4 +11,3 @@ drop table tools; drop table tasks; drop table history; drop table files; -drop table share; diff --git a/backend/internal/adapter/database/turso/migrations/000001_create_tables.up.sql b/backend/internal/adapter/database/turso/migrations/000001_create_tables.up.sql index dcf6abb..9671553 100644 --- a/backend/internal/adapter/database/turso/migrations/000001_create_tables.up.sql +++ b/backend/internal/adapter/database/turso/migrations/000001_create_tables.up.sql @@ -1,25 +1,16 @@ create table providers ( id text not null primary key, team_id text not null, + type text not null, name text not null, key text, url text, + models text, _date_created numeric not null default current_timestamp, _date_updated numeric, _version text not null ); -create table models ( - id text not null primary key, - team_id text not null, - provider_id text not null, - name text not null default '', - modelname text not null default '', - configuration text not null default '{}', - _date_created numeric not null default current_timestamp, - _date_updated numeric, - _version text not null -); create table settings ( id text not null primary key, @@ -150,14 +141,3 @@ create table files ( _date_updated numeric , _version text not null ); - -create table share ( - id text not null primary key, - object_id text not null, - object_type text not null, - team_id text not null, - enabled numeric not null default 0, - _date_created numeric not null default current_timestamp, - _date_updated numeric, - _version text not null -); diff --git a/backend/internal/adapter/database/turso/provider_repository.go b/backend/internal/adapter/database/turso/provider_repository.go new file mode 100644 index 0000000..6aea95c --- /dev/null +++ b/backend/internal/adapter/database/turso/provider_repository.go @@ -0,0 +1,61 @@ +package turso + +import ( + "database/sql" + "log" + + "gitea.trankilou.fr/fabien/lasebuche" + "trankilou.fr/lassistanoque/backend/internal/domain" +) + +type TursoModelRepository struct { + db *sql.DB + providerTable lasebuche.Table[domain.Provider] +} + +func NewTursoModelRepository(db *sql.DB) *TursoModelRepository { + dialect := lasebuche.NewSqliteDialect() + providerTable, err := lasebuche.NewTable[domain.Provider](db, dialect, "providers") + if err != nil { + log.Fatalf("error creating lasebuche team table") + } + + return &TursoModelRepository{ + db: db, + providerTable: providerTable, + } +} + +func (r *TursoModelRepository) ListProviders(userID string, teamID string) ([]*domain.Provider, error) { + return r.providerTable.SelectWhere( + "team_id=$1 and team_id in (select team_id from user_teams where user_id=$2)", + teamID, + userID, + ) +} + +func (r *TursoModelRepository) GetProvider(userID string, teamID string, id string) (*domain.Provider, error) { + return r.providerTable.SelectOne( + "id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)", + id, + teamID, + userID, + ) +} + +func (r *TursoModelRepository) CreateProvider(userID string, provider *domain.Provider) (*domain.Provider, error) { + return r.providerTable.Insert(provider) +} + +func (r *TursoModelRepository) UpdateProvider(userID string, provider *domain.Provider) (*domain.Provider, error) { + return r.providerTable.Update(provider) +} + +func (r *TursoModelRepository) DeleteProvider(userID string, teamID string, id string) error { + return r.providerTable.DeleteWhere( + "id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)", + id, + teamID, + userID, + ) +} diff --git a/backend/internal/adapter/database/turso/user_repository.go b/backend/internal/adapter/database/turso/user_repository.go index 94b8a53..4596b57 100644 --- a/backend/internal/adapter/database/turso/user_repository.go +++ b/backend/internal/adapter/database/turso/user_repository.go @@ -73,7 +73,7 @@ func (ur *TursoUserRepository) FindTeam(userid string, teamid string) (*domain.T } func (ur *TursoUserRepository) ListTeams(userid string) ([]*domain.Team, error) { - return ur.TeamTable.SelectWhere("user_id=$1", userid) + return ur.TeamTable.SelectWhere("id in (select team_id from user_teams where user_id=$1)", userid) } func (ur *TursoUserRepository) CreateTeam(userid string, team *domain.Team) (*domain.Team, error) { @@ -123,10 +123,6 @@ func (ur *TursoUserRepository) DeleteUserTeam(userid string, teamid string) erro return ur.UserTeamTable.Delete(teamid) } -func (ur *TursoUserRepository) PopulateUserWithTeams(user *domain.User) (*domain.UserWithTeams, error) { - return nil, nil -} - func (ur *TursoUserRepository) ListUserAddresses(id string) ([]*domain.UserAddress, error) { return ur.AddressTable.SelectWhere("user_id=$1", id) } diff --git a/backend/internal/adapter/llm/llm.go b/backend/internal/adapter/llm/llm.go new file mode 100644 index 0000000..da3ceb1 --- /dev/null +++ b/backend/internal/adapter/llm/llm.go @@ -0,0 +1,76 @@ +package llm + +import ( + "context" + "fmt" + + anyllm "github.com/mozilla-ai/any-llm-go" + "github.com/mozilla-ai/any-llm-go/providers/anthropic" + "github.com/mozilla-ai/any-llm-go/providers/ollama" + "github.com/mozilla-ai/any-llm-go/providers/openai" + "trankilou.fr/lassistanoque/backend/internal/domain" +) + +var providerTypes = []domain.Item{ + {ID: "anthropic", Text: "Anthropic"}, + {ID: "openai", Text: "OpenAI"}, + {ID: "ollama", Text: "Ollama"}, + {ID: "openaicomp", Text: "OpenAI compatible"}, + {ID: "openrouter", Text: "Openrouter"}, +} + +type AnyLLMEngine struct { +} + +func NewAnyLLMEngine() *AnyLLMEngine { + return &AnyLLMEngine{} +} + +func (e *AnyLLMEngine) ListProviderTypes() []domain.Item { + return providerTypes +} + +func providerFactory(provider *domain.Provider) (anyllm.Provider, error) { + switch provider.Type { + case "ollama": + return ollama.New( + anyllm.WithBaseURL(provider.URL), + ) + case "openai": + return openai.New( + anyllm.WithAPIKey(provider.APIKey), + ) + case "openaicomp": + return openai.New( + anyllm.WithBaseURL(provider.URL), + anyllm.WithAPIKey(provider.APIKey), + ) + case "anthropic": + return anthropic.New( + anyllm.WithAPIKey(provider.APIKey), + ) + } + return nil, fmt.Errorf("unknown provider type: %s", provider.Type) +} + +func (e *AnyLLMEngine) ListModelsFromProvider(ctx context.Context, provider *domain.Provider) ([]string, error) { + + models := make([]string, 0) + + prov, err := providerFactory(provider) + if err != nil { + return nil, err + } + if lister, ok := prov.(anyllm.ModelLister); ok { + response, err := lister.ListModels(ctx) + if err != nil { + return nil, err + } + for _, m := range response.Data { + models = append(models, m.ID) + } + } else { + models = append(models, "Default") + } + return models, nil +} diff --git a/backend/internal/domain/llm.go b/backend/internal/domain/llm.go new file mode 100644 index 0000000..8561108 --- /dev/null +++ b/backend/internal/domain/llm.go @@ -0,0 +1,26 @@ +package domain + +import "context" + +type LLMModelDetail struct { + ParentModel string `json:"parent_model"` + Format string `json:"format"` + Family string `json:"family"` + Families []string `json:"families"` + ParameterSize string `json:"parameter_size"` + QuantizationLevel string `json:"quantization_level"` +} + +type LLMModel struct { + Name string `json:"name"` + Model string `json:"model"` + ModifiedAt int64 `json:"modified_at"` + Size int64 `json:"size"` + Digest string `json:"digest"` + Detail *LLMModelDetail `json:"detail"` +} + +type LLMEngine interface { + ListProviderTypes() []Item + ListModelsFromProvider(ctx context.Context, provider *Provider) ([]string, error) +} diff --git a/backend/internal/domain/provider.go b/backend/internal/domain/provider.go new file mode 100644 index 0000000..0b0c2e1 --- /dev/null +++ b/backend/internal/domain/provider.go @@ -0,0 +1,24 @@ +package domain + +import "time" + +type Provider struct { + ID string `db:"id" json:"id"` + TeamID string `db:"team_id" json:"teamId"` + Type string `db:"type" json:"type"` + Name string `db:"name" json:"name"` + APIKey string `db:"key" json:"apiKey"` + URL string `db:"url" json:"baseUrl"` + Models string `db:"models" json:"models"` + DateCreated time.Time `db:"_date_created" json:"_date_created"` + DateUpdated *time.Time `db:"_date_updated" json:"_date_updated"` + VersionId string `db:"_version" json:"_version"` +} + +type ProviderRepository interface { + ListProviders(userID string, teamID string) ([]*Provider, error) + GetProvider(userID string, teamID string, id string) (*Provider, error) + CreateProvider(userID string, provider *Provider) (*Provider, error) + UpdateProvider(userID string, provider *Provider) (*Provider, error) + DeleteProvider(userID string, teamID string, id string) error +} diff --git a/backend/internal/domain/select.go b/backend/internal/domain/select.go new file mode 100644 index 0000000..8d77f39 --- /dev/null +++ b/backend/internal/domain/select.go @@ -0,0 +1,6 @@ +package domain + +type Item struct { + ID string `json:"id"` + Text string `json:"text"` +} diff --git a/backend/internal/domain/user.go b/backend/internal/domain/user.go index 9358d53..2cb7933 100644 --- a/backend/internal/domain/user.go +++ b/backend/internal/domain/user.go @@ -15,27 +15,27 @@ type User struct { Theme string `db:"theme" json:"theme"` Lang string `db:"lang" json:"lang"` Timezone string `db:"timezone" json:"timezone"` - DateCreated time.Time `db:"_date_created" json:"_date_created"` - DateUpdated *time.Time `db:"_date_updated" json:"_date_updated"` + DateCreated time.Time `db:"_date_created" json:"_dateCreated"` + DateUpdated *time.Time `db:"_date_updated" json:"_dateUpdated"` VersionId string `db:"_version" json:"_version"` } type Team struct { ID string `db:"id" json:"id"` Label string `db:"label" json:"label"` - DefaultModelID *string `db:"default_model_id" json:"default_model_id"` - DateCreated time.Time `db:"_date_created" json:"_date_created"` - DateUpdated *time.Time `db:"_date_updated" json:"_date_updated"` + DefaultModelID *string `db:"default_model_id" json:"defaultModelId"` + DateCreated time.Time `db:"_date_created" json:"_dateCreated"` + DateUpdated *time.Time `db:"_date_updated" json:"_dateUpdated"` VersionId string `db:"_version" json:"_version"` } type UserTeam struct { ID string `db:"id" json:"id"` - UserID string `db:"user_id" json:"user_id"` - TeamID string `db:"team_id" json:"team_id"` + UserID string `db:"user_id" json:"userId"` + TeamID string `db:"team_id" json:"teamId"` Administrator bool `db:"administrator" json:"administrator"` - DateCreated time.Time `db:"_date_created" json:"_date_created"` - DateUpdated *time.Time `db:"_date_updated" json:"_date_updated"` + DateCreated time.Time `db:"_date_created" json:"_dateCreated"` + DateUpdated *time.Time `db:"_date_updated" json:"_dateUpdated"` VersionId string `db:"_version" json:"_version"` } @@ -44,8 +44,8 @@ type UserAddress struct { UserID string `db:"user_id" json:"user_id"` Type string `db:"type" json:"type"` Address string `db:"address" json:"address"` - DateCreated time.Time `db:"_date_created" json:"_date_created"` - DateUpdated *time.Time `db:"_date_updated" json:"_date_updated"` + DateCreated time.Time `db:"_date_created" json:"_dateCreated"` + DateUpdated *time.Time `db:"_date_updated" json:"_dateUpdated"` VersionId string `db:"_version" json:"_version"` } @@ -55,8 +55,9 @@ type TeamRelation struct { } type UserWithTeams struct { - User *User `json:"user"` - Teams []*TeamRelation `json:"teams"` + User *User `json:"user"` + UserTeams []*UserTeam `json:"user_teams"` + Teams []*Team `json:"teams"` } func AddressTypes() []string { @@ -88,8 +89,6 @@ type UserRepository interface { UpdateUserTeam(userid string, team *UserTeam) (*UserTeam, error) DeleteUserTeam(userid string, teamid string) error - PopulateUserWithTeams(user *User) (*UserWithTeams, error) - ListUserAddresses(userid string) ([]*UserAddress, error) GetUserAddress(addrID string) (*UserAddress, error) CreateUserAddress(addr *UserAddress) (*UserAddress, error) diff --git a/backend/internal/http/handlers/provider.go b/backend/internal/http/handlers/provider.go new file mode 100644 index 0000000..fadffa0 --- /dev/null +++ b/backend/internal/http/handlers/provider.go @@ -0,0 +1,123 @@ +package handlers + +import ( + "net/http" + + "github.com/labstack/echo/v5" + "trankilou.fr/lassistanoque/backend/internal/domain" + "trankilou.fr/lassistanoque/backend/internal/service/auth" + "trankilou.fr/lassistanoque/backend/internal/service/provider" +) + +func NewModelGroup(prefix string, e *echo.Group, service *provider.Service, middlewares ...echo.MiddlewareFunc) *echo.Group { + modelHandler := &ModelHandler{ + providerService: service, + } + model := e.Group(prefix, middlewares...) + + model.GET("/providerTypes", modelHandler.ListProviderTypes) + model.GET("/:space", modelHandler.ListProviders) + model.POST("/:space/avalable-models", modelHandler.ListAvailableModels) + model.GET("/:space/:provider", modelHandler.GetProvider) + model.PUT("/:space", modelHandler.UpdateProvider) + model.POST("/:space", modelHandler.CreateProvider) + model.DELETE("/:space/:provider", modelHandler.DeleteProvider) + + return model +} + +type ModelHandler struct { + providerService *provider.Service +} + +func (h *ModelHandler) ListProviderTypes(c *echo.Context) error { + return c.JSON(http.StatusOK, h.providerService.ListProviderTypes()) +} + +func (h *ModelHandler) ListProviders(c *echo.Context) error { + userID := c.Get(auth.ContextUserIDKey).(string) + teamID := c.Param("space") + providers, err := h.providerService.ListProviders(userID, teamID) + if err != nil { + c.Logger().Error("error listing providers: %s", err) + return echo.NewHTTPError(http.StatusBadRequest, "error listing providers") + } + return c.JSON(http.StatusOK, providers) +} + +func (h *ModelHandler) GetProvider(c *echo.Context) error { + userID := c.Get(auth.ContextUserIDKey).(string) + teamID := c.Param("space") + providerID := c.Param("provider") + provider, err := h.providerService.GetProvider(userID, teamID, providerID) + if err != nil { + c.Logger().Error("error getting provider: %s", err) + return echo.NewHTTPError(http.StatusBadRequest, "error getting provider") + } + return c.JSON(http.StatusOK, provider) +} + +func (h *ModelHandler) CreateProvider(c *echo.Context) error { + userID := c.Get(auth.ContextUserIDKey).(string) + teamID := c.Param("space") + var provider domain.Provider + err := c.Bind(&provider) + if err != nil { + c.Logger().Error("error binding provider: %s", err) + return echo.NewHTTPError(http.StatusBadRequest, "error binding provider") + } + provider.TeamID = teamID + updprovider, err := h.providerService.CreateProvider(userID, &provider) + if err != nil { + c.Logger().Error("error creating provider: %s", err) + return echo.NewHTTPError(http.StatusBadRequest, "error creating provider") + } + return c.JSON(http.StatusOK, updprovider) +} + +func (h *ModelHandler) UpdateProvider(c *echo.Context) error { + userID := c.Get(auth.ContextUserIDKey).(string) + teamID := c.Param("space") + var provider domain.Provider + err := c.Bind(&provider) + if err != nil { + c.Logger().Error("error binding provider: %s", err) + return echo.NewHTTPError(http.StatusBadRequest, "error binding provider") + } + provider.TeamID = teamID + updprovider, err := h.providerService.UpdateProvider(userID, &provider) + if err != nil { + c.Logger().Error("error updating provider: %s", err) + return echo.NewHTTPError(http.StatusBadRequest, "error creating provider") + } + return c.JSON(http.StatusOK, updprovider) +} + +func (h *ModelHandler) DeleteProvider(c *echo.Context) error { + userID := c.Get(auth.ContextUserIDKey).(string) + teamID := c.Param("space") + providerID := c.Param("provider") + err := h.providerService.DeleteProvider(userID, teamID, providerID) + if err != nil { + c.Logger().Error("error creating provider: %s", err) + return echo.NewHTTPError(http.StatusBadRequest, "error creating provider") + } + return c.JSON(http.StatusOK, providerID) +} + +func (h *ModelHandler) ListAvailableModels(c *echo.Context) error { + teamID := c.Param("space") + var provider domain.Provider + err := c.Bind(&provider) + if err != nil { + c.Logger().Error("error binding provider: %s", err) + return echo.NewHTTPError(http.StatusBadRequest, "error binding provider") + } + provider.TeamID = teamID + list, err := h.providerService.ListAvailableModels(c.Request().Context(), &provider) + if err != nil { + c.Logger().Error("error listing models: %s", err) + return echo.NewHTTPError(http.StatusBadRequest, "error creating provider") + } + return c.JSON(http.StatusOK, list) +} diff --git a/backend/internal/http/handlers/user.go b/backend/internal/http/handlers/user.go index d2395d2..585683d 100644 --- a/backend/internal/http/handlers/user.go +++ b/backend/internal/http/handlers/user.go @@ -13,7 +13,6 @@ func NewUserGroup(prefix string, e *echo.Group, service *user.Service, middlewar userHandler := &UserHandler{ userService: service, } - _ = userHandler auth := e.Group(prefix, middlewares...) auth.GET("/me", userHandler.Me) auth.PUT("/:id", userHandler.Update) @@ -30,7 +29,7 @@ type UserHandler struct { func (h UserHandler) Me(c *echo.Context) error { userID := c.Get(auth.ContextUserIDKey).(string) - user, err := h.userService.GetUser(userID) + user, err := h.userService.GetUserWithTeams(userID) if err != nil { return echo.NewHTTPError(http.StatusUnauthorized, err.Error()) } diff --git a/backend/internal/http/router.go b/backend/internal/http/router.go index d228e8f..cd9cbe9 100644 --- a/backend/internal/http/router.go +++ b/backend/internal/http/router.go @@ -11,6 +11,7 @@ import ( "trankilou.fr/lassistanoque/backend/internal/config" "trankilou.fr/lassistanoque/backend/internal/http/handlers" "trankilou.fr/lassistanoque/backend/internal/service/auth" + "trankilou.fr/lassistanoque/backend/internal/service/provider" "trankilou.fr/lassistanoque/backend/internal/service/storage" "trankilou.fr/lassistanoque/backend/internal/service/user" ) @@ -32,10 +33,11 @@ type Router struct { } type Dependencies struct { - StorageService *storage.Service - AuthService *auth.Service - UserService *user.Service - TokenManager auth.TokenManager + StorageService *storage.Service + AuthService *auth.Service + UserService *user.Service + ProviderService *provider.Service + TokenManager auth.TokenManager } func NewRouter(deps Dependencies) *Router { @@ -71,6 +73,7 @@ func NewRouter(deps Dependencies) *Router { _ = handlers.NewAuthGroup("/auth", api, deps.AuthService) _ = handlers.NewUserGroup("/user", api, deps.UserService, deps.TokenManager.TokenMiddleware) _ = handlers.NewMiscGroup("/misc", api, deps.TokenManager.TokenMiddleware) + _ = handlers.NewModelGroup("/provider", api, deps.ProviderService, deps.TokenManager.TokenMiddleware) return &Router{ echo: e, diff --git a/backend/internal/service/provider/service.go b/backend/internal/service/provider/service.go new file mode 100644 index 0000000..813dd36 --- /dev/null +++ b/backend/internal/service/provider/service.go @@ -0,0 +1,76 @@ +package provider + +import ( + "context" + "fmt" + + "trankilou.fr/lassistanoque/backend/internal/domain" +) + +type Service struct { + repo domain.ProviderRepository + repoUser domain.UserRepository + llmengine domain.LLMEngine +} + +func NewService( + repo domain.ProviderRepository, + repoUser domain.UserRepository, + llmengine domain.LLMEngine, +) *Service { + return &Service{ + repo, + repoUser, + llmengine, + } +} + +func (s *Service) ListProviderTypes() []domain.Item { + return s.llmengine.ListProviderTypes() +} + +func (s *Service) ListProviders(userID string, teamID string) ([]*domain.Provider, error) { + return s.repo.ListProviders(userID, teamID) +} + +func (s *Service) GetProvider(userID string, teamID string, id string) (*domain.Provider, error) { + return s.repo.GetProvider(userID, teamID, id) +} + +func (s *Service) CreateProvider(userID string, provider *domain.Provider) (*domain.Provider, error) { + + if _, err := s.repoUser.FindUserTeam(userID, provider.TeamID); err != nil { + return nil, fmt.Errorf("error finding user in team: %s", err) + } + + return s.repo.CreateProvider(userID, provider) +} + +func (s *Service) UpdateProvider(userID string, provider *domain.Provider) (*domain.Provider, error) { + if _, err := s.repoUser.FindUserTeam(userID, provider.TeamID); err != nil { + return nil, fmt.Errorf("error finding user in team: %s", err) + } + + if _, err := s.repo.GetProvider(userID, provider.TeamID, provider.ID); err != nil { + return nil, fmt.Errorf("error finding provider: %s", err) + } + + return s.repo.UpdateProvider(userID, provider) +} + +func (s *Service) DeleteProvider(userID string, teamID string, id string) error { + if _, err := s.repoUser.FindUserTeam(userID, teamID); err != nil { + return fmt.Errorf("error finding user in team: %s", err) + } + + if _, err := s.repo.GetProvider(userID, teamID, id); err != nil { + return fmt.Errorf("error finding provider: %s", err) + } + + return s.repo.DeleteProvider(userID, teamID, id) + +} + +func (s *Service) ListAvailableModels(ctx context.Context, provider *domain.Provider) ([]string, error) { + return s.llmengine.ListModelsFromProvider(ctx, provider) +} diff --git a/backend/internal/service/user/service.go b/backend/internal/service/user/service.go index 29f27cb..67cfb5c 100644 --- a/backend/internal/service/user/service.go +++ b/backend/internal/service/user/service.go @@ -27,6 +27,30 @@ func (s *Service) GetUser(id string) (*domain.User, error) { return user, nil } +func (s *Service) GetUserWithTeams(id string) (*domain.UserWithTeams, error) { + u, err := s.GetUser(id) + if err != nil { + return nil, err + } + + teams, err := s.repo.ListTeams(u.ID) + if err != nil { + return nil, err + } + + userTeams, err := s.repo.ListUserTeams(u.ID) + if err != nil { + return nil, err + } + + ut := &domain.UserWithTeams{ + User: u, + UserTeams: userTeams, + Teams: teams, + } + return ut, nil +} + func (s *Service) UpdateUser(updUser *domain.User, fromAdmin bool) (*domain.User, error) { user, err := s.repo.FindUser(updUser.ID) if err != nil { diff --git a/backend/lassistanoque.db-wal b/backend/lassistanoque.db-wal index 5ecb379..a8b7f88 100644 Binary files a/backend/lassistanoque.db-wal and b/backend/lassistanoque.db-wal differ diff --git a/backend/web/src/lib/api/endpoints/provider.ts b/backend/web/src/lib/api/endpoints/provider.ts new file mode 100644 index 0000000..a263923 --- /dev/null +++ b/backend/web/src/lib/api/endpoints/provider.ts @@ -0,0 +1,13 @@ +import { api } from '../client'; +import type { Provider, Item } from "$lib/types/api" +import { space } from "$lib/state/space.svelte" + +export const providerApi = { + providerTypes: () => api.get('/provider/providerTypes'), + getProviders: () => api.get(`/provider/${space.id}`), + getProvider: (id: string) => api.get(`/provider/${space.id}/${id}`), + createProvider: (data: Partial) => api.post(`/provider/${space.id}`, data), + updateProvider: (data: Partial) => api.put(`/provider/${space.id}`, data), + deleteProvider: (id: string) => api.delete(`/provider/${space.id}/${id}`), + getAvailableModels: (data: Partial) => api.post(`/provider/${space.id}/avalable-models`, data), +}; diff --git a/backend/web/src/lib/api/endpoints/user.ts b/backend/web/src/lib/api/endpoints/user.ts index e4a8250..c2901c8 100644 --- a/backend/web/src/lib/api/endpoints/user.ts +++ b/backend/web/src/lib/api/endpoints/user.ts @@ -1,8 +1,8 @@ import { api } from '../client'; -import type { User, Address } from "$lib/types/api" +import type { User, Address, UserWithTeams } from "$lib/types/api" export const userApi = { - me: () => api.get('/user/me'), + me: () => api.get('/user/me'), update: (id: string, data: Partial) => api.put(`/user/${id}`, data), listAddresses: (id: string) => api.get(`/user/${id}/address`), diff --git a/backend/web/src/lib/components/FormError.svelte b/backend/web/src/lib/components/FormError.svelte index 8d7eccd..93a7af9 100644 --- a/backend/web/src/lib/components/FormError.svelte +++ b/backend/web/src/lib/components/FormError.svelte @@ -8,6 +8,4 @@ {#if errors.has(key)} {errors.get(key)} -{:else} -  {/if} diff --git a/backend/web/src/lib/components/Sidebar.svelte b/backend/web/src/lib/components/Sidebar.svelte index faf0a97..d209f38 100644 --- a/backend/web/src/lib/components/Sidebar.svelte +++ b/backend/web/src/lib/components/Sidebar.svelte @@ -3,6 +3,7 @@ import Menu from "$lib/components/Menu.svelte" import { logout } from "$lib/state/auth.svelte" import logo from "$lib/assets/logo-txt.png" + import { space } from '$lib/state/space.svelte' let {handleMenuDesktop, handleMenuMobile} = $props(); @@ -12,7 +13,7 @@ -
+
{#if uiState.menuContentVisible}
logo @@ -25,24 +26,38 @@
+{#if uiState.menuContentVisible} +
+
+ +
+
+{/if} -
+
{#if uiState.menuContentVisible} -
-
Fabien Masson
-
- Modifier +
+
+
+ FM +
+
Fabien Masson
-
-
-
diff --git a/backend/web/src/lib/state/auth.svelte.ts b/backend/web/src/lib/state/auth.svelte.ts index 4f086fd..a9ea9a4 100644 --- a/backend/web/src/lib/state/auth.svelte.ts +++ b/backend/web/src/lib/state/auth.svelte.ts @@ -1,15 +1,13 @@ -import { type User } from "$lib/types/api" +import type { User } from "$lib/types/api" type AuthState = { - user: User | null; - status: 'loading' | 'authenticated' | 'unauthenticated'; - //accessToken: string | null + user: User | null + status: 'loading' | 'authenticated' | 'unauthenticated' } export const auth: AuthState = $state({ user: null, status: 'loading', - //accessToken: null }) export const logout = () => { diff --git a/backend/web/src/lib/state/space.svelte.ts b/backend/web/src/lib/state/space.svelte.ts new file mode 100644 index 0000000..78275d4 --- /dev/null +++ b/backend/web/src/lib/state/space.svelte.ts @@ -0,0 +1,6 @@ +import type { Team } from "$lib/types/api" + +export const space = $state({ + spaces: [] as Team[], + id: "", +}); diff --git a/backend/web/src/lib/types/api.ts b/backend/web/src/lib/types/api.ts index 185e268..9a3134d 100644 --- a/backend/web/src/lib/types/api.ts +++ b/backend/web/src/lib/types/api.ts @@ -1,3 +1,8 @@ +export type Item = { + id: string + text: string +} + export interface User { id: string firstname: string @@ -9,9 +14,54 @@ export interface User { _version: string } +export interface UserTeam { + id: string + userId: string + teamId: string + administrator: boolean + _version: string +} + +export interface Team { + id: string + label: string + defaultModelId: string + _version: string +} + +export interface UserWithTeams { + user: User + userTeams: UserTeam[] + teams: Team[] +} + export interface Address { id: string type: string address: string _version: string } + +export interface Provider { + id: string + teamId: string + type: string + name: string + apiKey: string + baseUrl: string + models: string + _date_created: string + _date_updated: string + _version: string +} + +export interface Model { + id: string + teamId: string + providerId: string + modelName: string + configuration: object + _date_created: string + _date_updated: string + _version: string +} diff --git a/backend/web/src/lib/utilities/validators.ts b/backend/web/src/lib/utilities/validators.ts index ee66096..95d8095 100644 --- a/backend/web/src/lib/utilities/validators.ts +++ b/backend/web/src/lib/utilities/validators.ts @@ -1,5 +1,10 @@ const emailRegex: RegExp = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; +const urlRegex: RegExp = /^https?:\/\/([a-zA-Z0-9.-]+|\[[0-9a-fA-F:.]+\])(:\d+)?(\/[^\s]*)?$/; export function validateEmail(input: string): boolean { return emailRegex.test(input); } + +export function validateUrl(input: string): boolean { + return urlRegex.test(input); +} diff --git a/backend/web/src/routes/(authenticated)/(chat)/+page.svelte b/backend/web/src/routes/(authenticated)/(chat)/+page.svelte index b872b49..0b1bff7 100644 --- a/backend/web/src/routes/(authenticated)/(chat)/+page.svelte +++ b/backend/web/src/routes/(authenticated)/(chat)/+page.svelte @@ -1,7 +1,8 @@ - +
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur? Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur? Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur? Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur? Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur? Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur? +
diff --git a/backend/web/src/routes/(authenticated)/+layout.svelte b/backend/web/src/routes/(authenticated)/+layout.svelte index a4444c7..63d3b0b 100644 --- a/backend/web/src/routes/(authenticated)/+layout.svelte +++ b/backend/web/src/routes/(authenticated)/+layout.svelte @@ -3,6 +3,7 @@ import { page } from '$app/state'; import {uiState} from "$lib/state/ui.svelte" import { auth } from '$lib/state/auth.svelte' + import { space } from '$lib/state/space.svelte' import Sidebar from "$lib/components/Sidebar.svelte" import { goto } from '$app/navigation' import { onMount } from "svelte"; @@ -39,7 +40,9 @@ onMount(async () => { handleResize() if (data.me) { - auth.user = data.me + auth.user = data.me.user + space.spaces = data.me.teams + space.id = data.me.teams[0].id themeToLocalStorage(auth.user.theme) applyTheme() auth.status = 'authenticated' @@ -64,7 +67,7 @@
-
+

{page.data.title} {#if page.data.back} diff --git a/backend/web/src/routes/(authenticated)/profile/+page.svelte b/backend/web/src/routes/(authenticated)/profile/+page.svelte index 321f77f..ca0af48 100644 --- a/backend/web/src/routes/(authenticated)/profile/+page.svelte +++ b/backend/web/src/routes/(authenticated)/profile/+page.svelte @@ -2,7 +2,7 @@ import { SvelteMap } from 'svelte/reactivity'; import type { PageProps } from './$types'; - import type { User, Address } from '$lib/types/api' + import type { User, Address, Item } from '$lib/types/api' import { userApi } from '$lib/api/endpoints/user' import { miscApi } from '$lib/api/endpoints/misc' import FormError from '$lib/components/FormError.svelte'; @@ -13,15 +13,15 @@ let { data }: PageProps = $props(); - let form : User = $state(data.me) + let form : User = $state(data.me.user) let addresses : Address[] = $state([] as Address[]) let newAddress : Address = $state({} as Address) - let timezones = $state([] as string[]) + let timezones = $state([] as Item[]) let errors = new SvelteMap() onMount(async ()=> { addresses = await userApi.listAddresses(form.id) - timezones = await miscApi.listTimezones() + timezones = (await miscApi.listTimezones()).map((it)=>{return {id:it, text:it}}) }) $effect(()=>{ @@ -64,24 +64,26 @@ -
+

Mon compte

{errors.clear()}}> - - - +
+ + + +
-
-
- +
+
+
-
- +
+
-
+
-
+
- +
-
+
+ +
+ +
+ + + +
+ +
+ + + +
+ +
+ + +
    + {#each availableModels as model (model.value)} + {#if model.value} +
  1. + +
  2. + {/if} + {/each} +
+
+
+ +
+ + +
+ +
+
diff --git a/backend/web/src/routes/layout.css b/backend/web/src/routes/layout.css index 5531ffb..0184019 100644 --- a/backend/web/src/routes/layout.css +++ b/backend/web/src/routes/layout.css @@ -8,35 +8,45 @@ html { @apply h-full; } body { - @apply h-full bg-stone-100 text-stone-900 dark:bg-stone-900 dark:text-stone-200; + @apply h-full bg-stone-100 text-stone-950 dark:bg-stone-950 dark:text-stone-100; } -label { +.formelement > label { @apply text-xs text-stone-600 uppercase dark:text-stone-400; } hr { @apply border-stone-200 dark:border-stone-800; } h2 { - @apply mb-4 font-bold; + @apply mb-4 text-base font-bold; +} + +.container { + @apply mx-auto max-w-3xl; } .pointer { @apply cursor-pointer; } .bt { + appearance: button; @apply cursor-pointer rounded bg-stone-300 px-4 py-1 font-bold hover:bg-stone-200 dark:bg-stone-800 dark:hover:bg-stone-700; + &.small { + @apply px-2 py-1; + } } .menuitem { - @apply text-stone-800 text-stone-950 dark:text-stone-300 hover:dark:text-stone-50; + @apply text-stone-950 dark:text-stone-300 hover:dark:text-stone-50; } .header { - @apply p-2 text-lg; + @apply py-2 text-lg font-bold; + /* h1 { - @apply rounded bg-stone-200 px-2 font-bold text-sky-600 dark:bg-stone-800 dark:text-sky-200; + @apply rounded bg-stone-200 px-2 font-bold uppercase dark:bg-stone-800; } + */ &.settings { h1 { @@ -53,10 +63,6 @@ h2 { @apply text-sky-500; } -.formgroup { - @apply rounded border border-stone-300 bg-stone-100 p-2 dark:border-stone-700 dark:bg-stone-900; -} - #outer { @apply flex h-full flex-row; } @@ -94,7 +100,7 @@ h2 { @apply ml-10; } #sidebar { - @apply fixed left-0 h-full w-full bg-stone-100 dark:bg-stone-900; + @apply fixed left-0 h-full w-full bg-stone-100 dark:bg-stone-950; } .mobileMenuClosed { #sidebar { @@ -103,14 +109,24 @@ h2 { } } -/* svelte select */ +/* form */ -.textinput { - @apply h-8 w-full rounded border-stone-700 bg-white px-2 py-1 text-stone-900 dark:bg-stone-800 dark:text-stone-100; +.formgroup { + @apply rounded border border-stone-300 bg-stone-100 p-2 text-sm dark:border-stone-700 dark:bg-stone-900; } +.formelement { + @apply mb-3; +} + +.textinput { + @apply h-8 w-full rounded border-stone-700 bg-stone-50 px-2 py-1 text-sm text-stone-900 dark:bg-stone-950 dark:text-stone-100; +} + +/* svelte select */ + .svelte-select { - @apply relative flex h-8 w-full border-none text-stone-900 dark:bg-stone-800 dark:text-stone-100; + @apply relative flex h-8 w-full border-none text-stone-900 dark:bg-stone-950 dark:text-stone-100; } .svelte-select .value-container { @@ -187,7 +203,7 @@ h2 { } .svelte-select-list .list-group-title { - @apply h-8 cursor-default px-5 text-sm leading-10 font-medium overflow-ellipsis whitespace-nowrap text-gray-400 uppercase; + @apply h-8 cursor-default px-5 text-sm leading-10 font-medium text-ellipsis whitespace-nowrap text-gray-400 uppercase; } .svelte-select-list .empty { @@ -195,7 +211,7 @@ h2 { } .svelte-select-item { - @apply h-8 cursor-default overflow-hidden px-5 leading-10 overflow-ellipsis whitespace-nowrap text-gray-800; + @apply h-8 cursor-default overflow-hidden px-5 leading-10 text-ellipsis whitespace-nowrap text-gray-800; } .item.group-item { @@ -223,7 +239,7 @@ h2 { } .multi-item { - @apply mt-1 mr-1 flex h-8 max-w-full cursor-default items-center overflow-hidden rounded-sm border border-gray-200 bg-gray-100 pr-1 pl-1 leading-8 overflow-ellipsis whitespace-nowrap; + @apply mt-1 mr-1 flex h-8 max-w-full cursor-default items-center overflow-hidden rounded-sm border border-gray-200 bg-gray-100 pr-1 pl-1 leading-8 text-ellipsis whitespace-nowrap; } .multi-item.disabled { diff --git a/backend/web/static/assets/fontello/config.json b/backend/web/static/assets/fontello/config.json index 3fb6ec7..2dac8a1 100644 --- a/backend/web/static/assets/fontello/config.json +++ b/backend/web/static/assets/fontello/config.json @@ -119,6 +119,18 @@ "css": "cancel", "code": 59401, "src": "fontawesome" + }, + { + "uid": "44e04715aecbca7f266a17d5a7863c68", + "css": "plus", + "code": 59402, + "src": "fontawesome" + }, + { + "uid": "f48ae54adfb27d8ada53d0fd9e34ee10", + "css": "trash-empty", + "code": 59403, + "src": "fontawesome" } ] } \ No newline at end of file diff --git a/backend/web/static/assets/fontello/css/fontello-codes.css b/backend/web/static/assets/fontello/css/fontello-codes.css index 819938a..7482433 100644 --- a/backend/web/static/assets/fontello/css/fontello-codes.css +++ b/backend/web/static/assets/fontello/css/fontello-codes.css @@ -9,6 +9,8 @@ .icon-logout:before { content: '\e807'; } /* '' */ .icon-left-big:before { content: '\e808'; } /* '' */ .icon-cancel:before { content: '\e809'; } /* '' */ +.icon-plus:before { content: '\e80a'; } /* '' */ +.icon-trash-empty:before { content: '\e80b'; } /* '' */ .icon-facebook:before { content: '\f09a'; } /* '' */ .icon-tasks:before { content: '\f0ae'; } /* '' */ .icon-menu:before { content: '\f0c9'; } /* '' */ diff --git a/backend/web/static/assets/fontello/css/fontello-embedded.css b/backend/web/static/assets/fontello/css/fontello-embedded.css index c9d9a5e..fd801f6 100644 --- a/backend/web/static/assets/fontello/css/fontello-embedded.css +++ b/backend/web/static/assets/fontello/css/fontello-embedded.css @@ -1,15 +1,15 @@ @font-face { font-family: 'fontello'; - src: url('../font/fontello.eot?82712141'); - src: url('../font/fontello.eot?82712141#iefix') format('embedded-opentype'), - url('../font/fontello.svg?82712141#fontello') format('svg'); + src: url('../font/fontello.eot?20532310'); + src: url('../font/fontello.eot?20532310#iefix') format('embedded-opentype'), + url('../font/fontello.svg?20532310#fontello') format('svg'); font-weight: normal; font-style: normal; } @font-face { font-family: 'fontello'; - src: url('data:application/octet-stream;base64,d09GRgABAAAAABoMAA8AAAAAKcQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABWAAAADsAAABUIIslek9TLzIAAAGUAAAARAAAAGA+I1PQY21hcAAAAdgAAADWAAACtG86egpjdnQgAAACsAAAAAsAAAAOAAAAAGZwZ20AAAK8AAAG7QAADgxiLvl6Z2FzcAAACawAAAAIAAAACAAAABBnbHlmAAAJtAAADOsAABH8gYXPKWhlYWQAABagAAAAMwAAADYxfVHyaGhlYQAAFtQAAAAgAAAAJAhaBIFobXR4AAAW9AAAADQAAABQRtv//WxvY2EAABcoAAAAKgAAACox5C4WbWF4cAAAF1QAAAAgAAAAIAFMDsduYW1lAAAXdAAAAXUAAALNzZ0dHnBvc3QAABjsAAAAogAAANl0GvjNcHJlcAAAGZAAAAB6AAAAnH62O7Z4nGNgZGBg4GIwYLBjYHJx8wlh4MtJLMljkGJgYYAAkDwymzEnMz2RgQPGA8qxgGkOIGaDiAIAJjsFSAB4nGNgYe5mnMDAysDAVMW0h4GBoQdCMz5gMGRkAooysDIzYAUBaa4pDAdeMHy8xRz0P4shinkNwzSgMCOKIiYAlDcNenic5ZLNbQIxEEbfhs0mEPKHpdSQAw3SRApAQqICCsgpVzpAVDBHj8SdfN6ZG9sBs3qW/GnlseYZeARmYi166P7oaPWrtBvzGYsx7/nR/osPJYPN67Ye6tGLb3znez/5+XoFYzqfqE5nfd98LX9Qh143G3jimbn6v7DklTfe1f2TFUW/DZOn3lctx/WSu9LmHzR3lmieWNJcW9J8W9LegSWaO5bIAJbIBZbICpa091G3gUxRD0G7XT0GsoeXQB7xTSCj+C6QW3wfyDJ+CuQbPweUf9fFV+AAAHicY2BABgAADgABAHicrVdrWxvHFZ7VDYwBA0LYzbruKGNRlx3JJHEcYisO2WVRHCWpwLjddW67SLhNk16S3ug1vV+UP3NWtE+db/lpec/MSgEH3KfPUz7ovDPzzpzrnFlIaEnifhTGUnYfifmdLlXuPYjohkvX4uShHN6PqNBIP5sW06LfV/tuvU4iJhGorZFwRJD4TXI0yeRhkwpa1VW9SUUtB0fF5ZrwA6oGMkn8rLAc+FmjGFAh2DuUNKsAgnRApd7hqFAo4BiqH1yu8+xovub4lyWg8kdVp4o1RaIXHcSjFadgFJY0FT2qBRHro5UgyAmuHEj6vEel1Qeja85cEPZDqoRRnYqNePetCGR3GEnq9TC1CTZtMNqIY5lZNiy6hql8JGmd19eZ+XkvkojGMJU004sSzEhem2F0k9HNxE3iOHYRLZoN+iR2IxJdJtcxdrt0hdGVbvpoQfSZ8ags9uN4kMbkeHGcexDLAfxRftykspawoNRI4dNU0ItoSvk0rXxkAFuSJlVMuBEJOcim9n3Ji+yua83nXxJJ2KfyWh2LgRzKIXRl6+UGIrQTJT033Y0jFddjSZv3Iqy5HJfclCZNaToXeCNRsGmexlD5CuWi/JQK+w/J6cMQmlpr0jkt2dp5uFUS+5JPoM0kZkqyZayd0aNz8yII/bX6pHDO65OFNGtPcTyYEMD1RIZDlXJSTbCFywkh6cLIsZVIrUq3rIq5M7bTVewS7peuHd80r41DR3OzohhCi6vq8RqK+ILOCoWQBulWkxY0qFLSheA1PgAAGaIFHu1itGDytYiDFkxQJGLQh2ZaDBI5TCQtImxNWtLdvSgrDbbiqzR3oA6bVNXdnah7z066dcxXzfyyzsRScD/KlpYCclKfFj2+cigtP7vAPwv4IWcFuSg2elHG4YO//hAZhtqFtbrCtjF27TpvwU3mmRiedGB/B7Mnk3VGCjMhqgrxQkHdGTmOY7JV0yIThXAvoiXly5DmUX5ziHDiy+Q/ly45YlFUhe/7HIFlrDlptjzt0aee+zTCtQIfa16TLurMYXkJ8Wb5NZ0VWT6lsxJLV2dllpd1VmH5dZ1Nsbyis2mW39DZOZaeVuP4UyVBpJVskfMO35Ym6WOLK5PFj+xi89ji6mTxY7sotaAL3pl+wql/W1fZz+P+1eGfhF1Pwz+WCv6xvAr/WDbgH8tV+Mfym/CP5TX4x/Jb8I/lGvxj2dKybQr2uobaS4lE03OSwKQUl7DFNbuu6bpH13Efn8FV6MgzsqnSDcWN/YkMl71/dpzibL4ScsXRM2tZ2amFEZoie/ncsfCcxbmh5fPG8udxmuWEX9WJa3uqLTwvVv4l+G/rjtrIbjg19vUm4gEHTrcflyXdaNILunWx3aSN/0ZFYfdBfxEpEisN2ZIdbgkI7d3hsKM66CERHj50XbxIG45TW0aEb6F3rdBF0Epopw1Dy2aFT+cD72DYUlK2hzjz9kmabNnzqKL8MVtSwj1lcyc6KsmydI9Kq+WnYp877QyatjI71HZCleDx65pwt7OvUilIBorKeFSxXApSFzjhTvf4nhSmof+rbeRYQcM2v1gzgdGC805RomxPraCJIBllFFz5K6fiRDaiwUYU8Zt30i91oRDa41hIzJZX81ioNsL00mSJZsz6tuqwUs7inUkI2RkbaRJ7UUu28aCz9fmkZLvyVFClgdHd498uNomnVXueLcUl//IxS4JxuhL+wHnc5XGKN9E/WhzFbboYRD0Xb6psx61s3VnGvX3lxOqu2zux6p+690k7Ak23vCcp3NJ02xvCNq4xOHUmFQlt0Tp2hMZlrs9VG/kUH2i+dZ0LVOH6tHDz7PnbOpvBWzPe8j+WdOf/VcXsE/extkKrOlYv9Ti3s4MGfMsbR+VVjG57dZXHJfdmEoK7CEHNXnt8jeCGV1t0E7f8tTPmuzjOWa7SC8Cva3oR4g2OYohwy208vONovam5oOkNwG/rkRDbAD0Ah8GOHjlmZhfAzNxjTgdgjzkM7jOHwXeYw+C7+gi9MACKgByDYn3k2LkHQHbuLeY5jN5mnkHvMM+gd5ln0HusMwRIWCeDlHUy2GedDPrMeRVgwBwGB8xh8JA5DL5n7NoC+r6xi9H7xi5GPzB2MfrA2MXoQ2MXox8auxj9yNjF6MeIcXuSwJ+YEW0CfmThK4Afc9DNyMfop3hrc87PLGTOzw3HyTm/wOaXJqf+0ozMjkMLecevLGT6r3FOTviNhUz4rYVM+B24dybn/d6MDP0TC5n+BwuZ/kfszAl/spAJf7aQCX8B9+XJeX81I0P/m4VM/7uFTP8HduaEf1rIhKGFTPhUj86bL1uquKNSoRjinya0wdj3aPqAild7h+PHuvkFJdoBQQAAAAABAAH//wAPeJydV1tsW9l1Pfuce899kCIp8j4oiaLFN01JlMSnLck0rSct0ZKskSVTM5XtJjNSZKPjmVEdx3BcJ2M58Y8dOJMWeSCxM3JSINNgHBj9KJKgaJx+zNcUDTz9aPIR9GOAopgiBQLMYExnn0s69aBNW5SPe3lel2fvvdbe6xBKyJO32fv0ZyRB5snh6vjRECVwGBjZD5Sxug4M5nNAjxIgsEsII7uEUcoahDF6hlBGj09UK+OxaEzidj8YHohFU1zhSiyaLCYLpeJhWlLKFVrOQqpQgVwYbIMrYdgH1j5mKB5IJfHNcZGVz5XKFZyVCgPzbz84O5ybOWH2UO4GChJjEJI9Hml6AbYfPHqw/WhpSnarPZokSwyoroSM1Znc8F+c7+tc35uYrkNHbQXuHbtW10ZsWdIVkCRgHB8SloN+X3pncuFavX7tnycvpDyWHnExmetAVR0k2R7R5lhuqHB9rj+Tukj4kydPtqUhtkw6SY6MkSmySk6Rl6vnFj1oO9Q7QXNpu8SlunZ9oFJ114tuo7sKyOgwGa66dQqcAL9CuISfK0SSrnnQu4Q18MbImQ5Anx7f+KP1xurK8tL83NHZyYnDldGDXabRVY4FIj4/39cPjj/LhTz6r5wrc9MAg8ci0ST6VMZOWsGQlcq5sGTjQBT9nrPwRxYq1JINnoolU6ViQbi4AnYu9fsp8+uj84NVmJYyE5FkgtHXlyebwaklkNy+vuRohMeztaWZrv0+NXog2efzwOO/X7mwgh+4ceXtq/Tyg0swWcnOH1wfZIlEZCotTc+2xzfY1MDAz61u6DB9C83nJxYWJsIHJg4UklYw1E0tX7dOrWThwESI3hoTC1aaHzSu0Et/fZFf+8f+LEyxIws+syMYhPYw+og8ucDuMBdRiUGyJF8dTiIq0ik/oxIBdGKdMIntEolKu4QSilgFxCrAhVBsMOiXeU9/uZD00DCUEsCjQ5AslCEMCQeDwh3lnGVbhhdMu4S+zFmPjLBBg91BeKn5ujlqjZkmXLZW4OsdPV9c3Lp9e6tvpkvT3jxHM/MRr97rsai/zy91/7b5umEcMsYsuFxe+aWVmN+A2+9+hRo+7lc2Pj9OuwYNneBL8O4+W3fs2SbT1YkXG/UjEpHGEC+kkO7xIazRJFmSdzlg/1NzGAj+oZ2UXnjh+eeOH631Z6J9Ab/Cg/1QSEY9YOVKCdPgXsSMMMgxbwgEJXk/ICmRbgqPCtoVkG7l5BAgNYsFYXS53ZnP7cMGvscgZyFT7XIpZ7cfpmBHGOjY8sVlurqzCiFV2dJdgTSXvUsdinKsq1tTJN9l1e3rsRe5j89Ykqymda/6kqKCLm+pHjvRmqseC3ZrKuu8rLjBG7IXZa9SMyRJa03WYaMV+oti3Bc2e3Lcw80lkMc71HrIpyubmntc5tWw7OHunDfU4wW34szt6u4bVNyKsfTMVNeYLE+G2lO7feB+GgP6DnMTk4RJohpFwsq7DECCT+CIXAjYnZ12jvNQf6LFu2Khwmwrh4xjDNmUhRL65J2ZkWZ8ZEYPZiqDR9+Zy0wkQ+otJIn0xbeuTY+fPDk+vLI6noZaLVlZWYW/PXnFIREhMu7jQ/YviAVOvOQgqZAaKFWzWhnzYYpRiFRAfNP6EdzX/Pzb+tLJ6jBREOyKdJUQCcdeRXpICP9NFRtcBr5FZErlNSLL9BShMl3smX/bhev2t+cr8H9YYP+//qg68uwSpObV/3VNo9GoWoRMTR4aH8nuT4Z7rAB6ghsaVpREOaWYeROSiGcLIRow2wHoRMZGcnYYcCCVxC6udBqWHcmVDkEBJ1rMhlgRlBRiFyFswL9XT1SLYGraQ82P3/jGZHN4cmNjEt6NhTWm9Kh6h7s5nChAKQ7vJgpyXA2W7zZv3KUv5+/mfQO+E76fHDlxZF8Jbj99RPOn260HTGyARwrwkCqxQqL9jBkFn6DCrTvNG3cgW7hb8HpP+AaQxOTJl1mR/hsiLl6NtLiMvdcJBaBrSGs4RbDgLVqJpCHxbiypSjtdHQa0G40r2ZGUaCEnwWJFo/k1a9TMWNabzc/2xuO9cONNy8pgxoKzBoyGjeYbhpExx803M+HZcAbuGePYNJtfMxz8f8jOYu1PkSqZqB428I+hTjgGhctXicyozLBiMYweuYqbpBzopmiwNSz/WAcxtItWrCuRSWBO6UVyWF7AFBFrxUdkD9N2ulKxdnxEsWqZhKbEWvaU85h+LDQGXqS4A1X9kXDrj7AcQ6vpOPQBp5xS7mr+gytkfeSxLM9HVsgFQ64s9cseTQKMxJcnN3TVpXFMLpgMmxdxoURVGPC4mu/qhveu5XnkNeAuJmsNO0QebsWjgfHodeIhireQOtdFxXnWzKRtxZx4OAB0KgVmUaeKtADmhUjOYo14b/Oze6aZMcdM4Xk4h5FAf+/Bjd44bGAE9jAA/e0BOGua+61Ray/9dC8f0F/Q6yROwtWeaJdPkRAWdRQ4mIeQPeSCETIcVCScvx8C5Sk02ojYB+Wnu7Et+gtvHYG7t4eXuk/cff/Z9nr39rznLfHj3j3vf53ozYoJzp7uUxv1jyFqLrY5IgRRex0pirqGbMoYJAprsgjWKQkDRhdjnfgKRAQkwMTI90JeiXXiuyginc9Re204umfd/MHNmztnltn8V9Pp7XvNk/CX9y5tvUrEgxGX72M+ZEQhLtJD+lF5naq+MACyqwC6FAKmGfjfUh1HZcklb2KG0TVJ3yQa7k1jm4htVaHqJq4HosAmPpSTNfQxPyVSy+JoOTeUSSeTCbHTTp8bNVYiUoyk8JvIF2MiyIqQV0XBtpwlsk6q3S6327F2O49tOoYq4RuP6Pjjh/Die+9B2PJ8fBqRZnnYHef2B1vV2qPZ92Yfv3/T6bnpRdkGnlsevHlviS7w3RJQFxdHA33I1ulDrBL7yBApVwtpVKwqGgsUtQIyVZaQn4gZhjzF0MGakEanBLQWUY4Xk3mnhj1jnbCGPWOd3W7Hnm03DN/HH7S23Cl2+Idbn551fs62tl5z7HCu4JkVdsyKBQLrkqN/7mLucZMukifrZL5aaywvzCJfO9AgRusaKKqyS4SolhxRjZYxQQMZhZDMuVNC+BmsKfz4iZVY73w56u8PBXQRSzyHZFkFxZ78+192IRmLOspH5CIjzMqohlkZNY2kRLNSCmt6WXxRBiJEbZRGlmkomJM80WzcTX19ubAfPvVM4+TCEXs1fTp1IjhRh3hpeS67kZ1bPhC9ErXmVrZXc/21Fxbqo4FY3Rs6tH5oef25udEXxnq99R9Y2TSN59KFqJQZCH6iMbnh5ty9MTlSG7DcVO1O12/vTPdXogZWUj0YPRCf3rnWGMxVDw4MGf5sBg5WRwYbbS2pIUf7yDB5raojKlg3yhhab8mFLkzl8m5bH4PkJLhdKvJJWxeYMvx3g//DQizZWiQSj/hjDqJQXOIRLhnFK3UoLk5zeSEaccQ0LBRJQoAq6NUw9ILBNHOoVuvJD8+s9564d+V0eifQdT49UeztHTDi9e9E4uGwkaTP/THQt2am/8TvT0LsS3/23WgybsQTvYXD0+fpF6IrsbCusOcu2lSleLhq6zmZY97oQERhtor3IDNCInvW96eCpoa5a05ouusONxqCG2cEN473hS2DCQVtWx6axbOUjNoOy5SMcEkIzWsotuXI58Mi45fKQicnsADLFvuVTk9/7+++d5rqUHWH3XSr4/EtylnzjWBYivX1ga6dVV2AvlKG/8btpq8wrGKvdMDV+8mJJM1U0vdfxLNpl1t+CPBa4GTZWDJ/o3R0KL8xl8zXpkflIMeBh7JTH/Asep/V0L4YKVXzfmR5AARTBF+wcn0G58gSEdkQg9/AoyacISgaj5uJWCrhE4GCCIJanNEFJzA+CpLBA0YYHC7E4K/gDeWlH3/u5Ld3JujM+e+ufv/in069VLtUw8/wciHtk/8DvpmIfu6n56ZevfPDO69OvXJ+cu7Sn1+aCyVLA+bTPW6zf0UshkQ9NY2AjjXM7wjYTx7LwqbFeFc/WApz0COKGWJIHEY0wJuoq+yfAtJN5lV1rjz+kt6pyC43wK8fv/VLgwXYHc1g9Q774+/7NJlt3eugkt/9M7rw+OPn1UGdBuU2LxrorxEyQ9araxlgci+orJqgkjqWp1zBCqIiulVUqxLKVuUKljShclD8EKbK6FIFQaNwUVAIRapRgRhKjuOJen/RjtlxTRzPDQ9FLdOqwZbd8maWYpppo0Zk0n0gDlZhKrKv6BPKZwyKLZnEGujQ1dM/HJfkOu+U9l+YHN1aytBs/dzLn04f6wzYH2FGHeisjX9jeQ1j0/yWrqI+0tXP60ocddIGdk4tH+Ue6RgK7OJotr69s13PphPHfIOok4IBf+1gZWrn23MfiUUxVf853n8HXoHb9gB4nGNgZGBgAOKCJdf54vltvjLwM78AijA8W9WuAaU1////n8XKxhwE5HIwMIFEAWnuDOUAeJxjYGRgYA76n8XAwMr6////v6xsDEARFCACAI/vBeR4nGN+wcDADMQs+v//My8AsiMZGJhOQcRAbJgYsyBQ3BpNHCjGygriA/W+AGKgOAB0wBARAAAAAACQAVQBvAJ+AsIDuAP+BIAExgUQBUoF1gY8BuIHZgfWCCgIbgj+AAAAAQAAABQAawAGAAAAAAACACQATgCNAAAAgA4MAAAAAHicdZDfTsIwFId/5Z8KiRpNvLVXBmIcsEQuSEhIMHCjN8Rwa8YY28hYSVdIeA3fwYfxJXwWf4xijMQtXb/z9fS0OwCu8AWB/fPIsWeBU0Z7LuAEPctF+ifLJfKz5TJqeLVcoX+zXMU9Qss1XOOdFUTpjNECH5YFLsWF5QLOxa3lIv2D5RK5Z7mMG/FiuULvW65iIjLLNdyJz4FabXUcRkbWBw3pttyOnG6loopTL5He2kRKZ7Iv5yo1QZIox1fLA4+DcJ14+hAe5kmgs1ilsu20DmoUpIH2TDDbVc82oWvMXM61WsqhzZArrRaBb5zImFW32fx9HgZQWGELjZitimAgUadtcHbR4uiQpsyQzNxnxUjhIaHxsOaOKF/JGPc55oxS2oAZCdmBz+/yyI9JIfcnrKKPVv/GE9LujDj3Em3WbR1ljUhpnunldvZz9wwbnubSGu7a3VLnt5IY/qkh2Y/d2oLGp3fyrhjaLpp8//m/b+ZxhFEAAAB4nG3IW27CMBBGYf/gmARa2nJZRhY1MYNt4XosX4TYPary2u/tHLVRq7363wUbbKExwGCHERP2OOADnzjiC9/4wQlnXHBV2npqQ69cqnkWTtZvrTj9NzTfQptKcL7NS3AmipPexsj3tS0ly3G8k+VF5DE0qo+qfzl140LzfRko58hHV+jWqQVJs6VsnIiLfMiUucw5UuKdD7VJeSn1Bpt1My8AAHicY/DewXAiKGIjI2Nf5AbGnRwMHAzJBRsZ2J02MjBoQWguFHonAwMDNxJrJwMzA4PLRhXGjsCIDQ4dESB+istGDRB/BwcDRIDBJVJ6ozpIaBdHAwMji0NHcghMAgQ2MvBp7WD837qBpXcjE4PLZtYUNgYXFwCUHCoHAAA=') format('woff'), - url('data:application/octet-stream;base64,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LLIAAEcrLbBULLIAAUcrLbBVLLIBAEcrLbBWLLIBAUcrLbBXLLMAAABDKy2wWCyzAAEAQystsFksswEAAEMrLbBaLLMBAQBDKy2wWyyzAAABQystsFwsswABAUMrLbBdLLMBAAFDKy2wXiyzAQEBQystsF8ssgAARSstsGAssgABRSstsGEssgEARSstsGIssgEBRSstsGMssgAASCstsGQssgABSCstsGUssgEASCstsGYssgEBSCstsGcsswAAAEQrLbBoLLMAAQBEKy2waSyzAQAARCstsGosswEBAEQrLbBrLLMAAAFEKy2wbCyzAAEBRCstsG0sswEAAUQrLbBuLLMBAQFEKy2wbyyxADwrLrEwARQrLbBwLLEAPCuwQCstsHEssQA8K7BBKy2wciywABaxADwrsEIrLbBzLLEBPCuwQCstsHQssQE8K7BBKy2wdSywABaxATwrsEIrLbB2LLEAPSsusTABFCstsHcssQA9K7BAKy2weCyxAD0rsEErLbB5LLEAPSuwQistsHossQE9K7BAKy2weyyxAT0rsEErLbB8LLEBPSuwQistsH0ssQA+Ky6xMAEUKy2wfiyxAD4rsEArLbB/LLEAPiuwQSstsIAssQA+K7BCKy2wgSyxAT4rsEArLbCCLLEBPiuwQSstsIMssQE+K7BCKy2whCyxAD8rLrEwARQrLbCFLLEAPyuwQCstsIYssQA/K7BBKy2whyyxAD8rsEIrLbCILLEBPyuwQCstsIkssQE/K7BBKy2wiiyxAT8rsEIrLbCLLLILAANFUFiwBhuyBAIDRVgjIRshWVlCK7AIZbADJFB4sQUBFUVYMFktAEu4AMhSWLEBAY5ZsAG5CAAIAGNwsQAHQrEAACqxAAdCsQAKKrEAB0KxAAoqsQAHQrkAAAALKrEAB0K5AAAACyq5AAMAAESxJAGIUViwQIhYuQADAGREsSgBiFFYuAgAiFi5AAMAAERZG7EnAYhRWLoIgAABBECIY1RYuQADAABEWVlZWVmxAA4quAH/hbAEjbECAESzBWQGAERE') format('truetype'); + src: url('data:application/octet-stream;base64,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') format('woff'), + url('data:application/octet-stream;base64,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') format('truetype'); } /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ @@ -17,7 +17,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'fontello'; - src: url('../font/fontello.svg?82712141#fontello') format('svg'); + src: url('../font/fontello.svg?20532310#fontello') format('svg'); } } */ @@ -67,6 +67,8 @@ .icon-logout:before { content: '\e807'; } /* '' */ .icon-left-big:before { content: '\e808'; } /* '' */ .icon-cancel:before { content: '\e809'; } /* '' */ +.icon-plus:before { content: '\e80a'; } /* '' */ +.icon-trash-empty:before { content: '\e80b'; } /* '' */ .icon-facebook:before { content: '\f09a'; } /* '' */ .icon-tasks:before { content: '\f0ae'; } /* '' */ .icon-menu:before { content: '\f0c9'; } /* '' */ diff --git a/backend/web/static/assets/fontello/css/fontello-ie7-codes.css b/backend/web/static/assets/fontello/css/fontello-ie7-codes.css index a8aeb37..0c2ad43 100644 --- a/backend/web/static/assets/fontello/css/fontello-ie7-codes.css +++ b/backend/web/static/assets/fontello/css/fontello-ie7-codes.css @@ -9,6 +9,8 @@ .icon-logout { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-left-big { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-trash-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-facebook { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-tasks { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-menu { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } diff --git a/backend/web/static/assets/fontello/css/fontello-ie7.css b/backend/web/static/assets/fontello/css/fontello-ie7.css index 1d5224e..dee2b0c 100644 --- a/backend/web/static/assets/fontello/css/fontello-ie7.css +++ b/backend/web/static/assets/fontello/css/fontello-ie7.css @@ -20,6 +20,8 @@ .icon-logout { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-left-big { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-trash-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-facebook { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-tasks { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-menu { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } diff --git a/backend/web/static/assets/fontello/css/fontello.css b/backend/web/static/assets/fontello/css/fontello.css index a7562cf..badfbb6 100644 --- a/backend/web/static/assets/fontello/css/fontello.css +++ b/backend/web/static/assets/fontello/css/fontello.css @@ -1,11 +1,11 @@ @font-face { font-family: 'fontello'; - src: url('../font/fontello.eot?90952797'); - src: url('../font/fontello.eot?90952797#iefix') format('embedded-opentype'), - url('../font/fontello.woff2?90952797') format('woff2'), - url('../font/fontello.woff?90952797') format('woff'), - url('../font/fontello.ttf?90952797') format('truetype'), - url('../font/fontello.svg?90952797#fontello') format('svg'); + src: url('../font/fontello.eot?1806785'); + src: url('../font/fontello.eot?1806785#iefix') format('embedded-opentype'), + url('../font/fontello.woff2?1806785') format('woff2'), + url('../font/fontello.woff?1806785') format('woff'), + url('../font/fontello.ttf?1806785') format('truetype'), + url('../font/fontello.svg?1806785#fontello') format('svg'); font-weight: normal; font-style: normal; } @@ -15,7 +15,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'fontello'; - src: url('../font/fontello.svg?90952797#fontello') format('svg'); + src: url('../font/fontello.svg?1806785#fontello') format('svg'); } } */ @@ -64,6 +64,8 @@ .icon-logout:before { content: '\e807'; } /* '' */ .icon-left-big:before { content: '\e808'; } /* '' */ .icon-cancel:before { content: '\e809'; } /* '' */ +.icon-plus:before { content: '\e80a'; } /* '' */ +.icon-trash-empty:before { content: '\e80b'; } /* '' */ .icon-facebook:before { content: '\f09a'; } /* '' */ .icon-tasks:before { content: '\f0ae'; } /* '' */ .icon-menu:before { content: '\f0c9'; } /* '' */ diff --git a/backend/web/static/assets/fontello/demo.html b/backend/web/static/assets/fontello/demo.html index 50fb7d2..9aee99a 100644 --- a/backend/web/static/assets/fontello/demo.html +++ b/backend/web/static/assets/fontello/demo.html @@ -146,11 +146,11 @@ } @font-face { font-family: 'fontello'; - src: url('./font/fontello.eot?42653923'); - src: url('./font/fontello.eot?42653923#iefix') format('embedded-opentype'), - url('./font/fontello.woff?42653923') format('woff'), - url('./font/fontello.ttf?42653923') format('truetype'), - url('./font/fontello.svg?42653923#fontello') format('svg'); + src: url('./font/fontello.eot?13627011'); + src: url('./font/fontello.eot?13627011#iefix') format('embedded-opentype'), + url('./font/fontello.woff?13627011') format('woff'), + url('./font/fontello.ttf?13627011') format('truetype'), + url('./font/fontello.svg?13627011#fontello') format('svg'); font-weight: normal; font-style: normal; } @@ -245,34 +245,42 @@
icon-cancel0xe809
+
+ icon-plus0xe80a +
+
+ icon-trash-empty0xe80b +
+
+
icon-facebook0xf09a
icon-tasks0xf0ae
-
-
icon-menu0xf0c9
icon-github0xf113
+
+
icon-apple0xf179
icon-graduation-cap0xf19d
-
-
icon-google0xf1a0
icon-paper-plane0xf1d8
+
+
icon-history0xf1da
diff --git a/backend/web/static/assets/fontello/font/fontello.eot b/backend/web/static/assets/fontello/font/fontello.eot index 11e6f23..834321e 100644 Binary files a/backend/web/static/assets/fontello/font/fontello.eot and b/backend/web/static/assets/fontello/font/fontello.eot differ diff --git a/backend/web/static/assets/fontello/font/fontello.svg b/backend/web/static/assets/fontello/font/fontello.svg index 88ab2d0..027acb8 100644 --- a/backend/web/static/assets/fontello/font/fontello.svg +++ b/backend/web/static/assets/fontello/font/fontello.svg @@ -26,6 +26,10 @@ + + + + diff --git a/backend/web/static/assets/fontello/font/fontello.ttf b/backend/web/static/assets/fontello/font/fontello.ttf index a2ef454..6e4e3b3 100644 Binary files a/backend/web/static/assets/fontello/font/fontello.ttf and b/backend/web/static/assets/fontello/font/fontello.ttf differ diff --git a/backend/web/static/assets/fontello/font/fontello.woff b/backend/web/static/assets/fontello/font/fontello.woff index 43dc1f8..7a434f9 100644 Binary files a/backend/web/static/assets/fontello/font/fontello.woff and b/backend/web/static/assets/fontello/font/fontello.woff differ diff --git a/backend/web/static/assets/fontello/font/fontello.woff2 b/backend/web/static/assets/fontello/font/fontello.woff2 index 711cc99..934d71e 100644 Binary files a/backend/web/static/assets/fontello/font/fontello.woff2 and b/backend/web/static/assets/fontello/font/fontello.woff2 differ