first commit

This commit is contained in:
2026-08-08 23:12:37 +02:00
parent ff6ef46cd4
commit e3fd942c47
20 changed files with 494 additions and 271 deletions
@@ -70,13 +70,7 @@ func (s *PasswordAuthenticator) Register(ctx context.Context, registration auth.
Enabled: true,
}
count, err := s.userRepository.CountUsers()
if err != nil {
return nil, err
}
if count == 0 {
user.Administrator = true
}
// TODO: create team "Espace personnel" and put user in it
user, err = s.userRepository.CreateUser(user)
@@ -1,13 +1,13 @@
package turso
import (
"database/sql"
"embed"
"fmt"
"github.com/golang-migrate/migrate/v4"
"github.com/golang-migrate/migrate/v4/database/sqlite3"
"github.com/golang-migrate/migrate/v4/source/iofs"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
"trankilou.fr/lassistanoque/backend/internal/config"
"trankilou.fr/lassistanoque/backend/internal/domain"
@@ -18,11 +18,11 @@ import (
var FS embed.FS
type TursoDB struct {
DB *sqlx.DB
DB *sql.DB
}
func NewTursoDB(cfg *config.Config) (*TursoDB, error) {
db, err := sqlx.Connect("turso", cfg.DatabaseURL)
db, err := sql.Open("turso", cfg.DatabaseURL)
if err != nil {
return nil, fmt.Errorf("error connecting to db %: %s", cfg.DatabaseURL, err)
}
@@ -36,7 +36,7 @@ func (db *TursoDB) Close() {
}
func (db *TursoDB) Migrate() error {
sqldb := db.DB.DB
sqldb := db.DB
dbDriver, err := sqlite3.WithInstance(sqldb, &sqlite3.Config{})
if err != nil {
return err
@@ -57,13 +57,13 @@ func (db *TursoDB) Migrate() error {
}
func (db *TursoDB) UserRepository() domain.UserRepository {
return &TursoUserRepository{DB: db.DB}
return NewTursoUserRepository(db.DB)
}
func (db *TursoDB) SettingsRepository() domain.SettingsRepository {
return &TursoSettingsRepository{DB: db.DB}
return NewTursoSettingsRepository(db.DB)
}
func (db *TursoDB) FileRepository() domain.FileRepository {
return &TursoFileRepository{DB: db.DB}
return NewTursoFileRepository(db.DB)
}
@@ -1,74 +1,47 @@
package turso
import (
"database/sql"
"io"
"log"
"time"
"github.com/jmoiron/sqlx"
"trankilou.fr/lassistanoque/backend/internal/adapter/database/dberrors"
"gitea.trankilou.fr/fabien/lasebuche"
"trankilou.fr/lassistanoque/backend/internal/domain"
"trankilou.fr/lassistanoque/backend/internal/utility"
)
type TursoFileRepository struct {
DB *sqlx.DB
db *sql.DB
FileTable lasebuche.Table[domain.File]
}
func NewTursoFileRepository(db *sql.DB) *TursoFileRepository {
dialect := lasebuche.NewSqliteDialect()
fileTable, err := lasebuche.NewTable[domain.File](db, dialect, "settings")
if err != nil {
log.Fatalf("error creating lasebuche team table")
}
return &TursoFileRepository{
db: db,
FileTable: fileTable,
}
}
func (fr *TursoFileRepository) FindByID(id string) (*domain.File, error) {
var file domain.File
err := fr.DB.Get(&file, "select id, name, content_type, storage_path, storage_filename, date_created, date_updated, _version from files where id=$1", id)
return &file, err
return fr.FileTable.Get(id)
}
func (fr *TursoFileRepository) Create(file *domain.File) (*domain.File, error) {
file.ID = utility.GenID()
file.VersionId = utility.GenID()
file.DateCreated = time.Now()
file.DateUpdated = time.Now()
_, err := fr.DB.NamedExec(
`insert into files (id, name, content_type, storage_path, storage_filename, date_created, date_updated, _version)
values (:id, :name, :content_type, :storage_path, :storage_filename, :date_created, :date_updated, :_version)`,
file)
if err != nil {
return nil, err
}
return file, nil
return fr.FileTable.Insert(file)
}
func (fr *TursoFileRepository) Update(file *domain.File) (*domain.File, error) {
newVersion := utility.GenID()
file.DateUpdated = time.Now()
res, err := fr.DB.Exec(
`update files
set name=$1,
content_type=$2,
storage_path=$3,
storage_filename=$4,
date_updated=$5,
_version=$6
where id=$7 and _version=$8`,
file.Name, file.ContentType, file.StoragePath, file.StorageFilename, file.DateUpdated,
newVersion, file.ID, file.VersionId,
)
if err != nil {
return nil, err
}
if count, _ := res.RowsAffected(); count == 0 {
return nil, dberrors.ErrNoRowUpdated
}
file.VersionId = newVersion
return file, nil
return fr.FileTable.Update(file)
}
func (fr *TursoFileRepository) Delete(id string) error {
res, err := fr.DB.Exec("delete from files where id=$1", id)
if count, _ := res.RowsAffected(); count == 0 {
return dberrors.ErrNoRowUpdated
}
return err
return fr.FileTable.Delete(id)
}
func (fr *TursoFileRepository) Upload(reader io.Reader, path string, name string, contentType string, replace bool) (*domain.File, error) {
@@ -90,18 +63,9 @@ func (fr *TursoFileRepository) Upload(reader io.Reader, path string, name string
Content: bytes,
}
_, err = fr.DB.NamedExec(
`insert into files (id, name, content_type, storage_path, storage_filename, date_created, date_updated, _version, content)
values (:id, :name, :content_type, :storage_path, :storage_filename, :date_created, :date_updated, :_version, :content)`,
file)
if err != nil {
return nil, err
}
return file, nil
return fr.FileTable.Insert(file)
}
func (fr *TursoFileRepository) Download(id string) (*domain.File, error) {
var file domain.File
err := fr.DB.Get(&file, "select * from files where id=$1", id)
return &file, err
return fr.FileTable.Get(id)
}
@@ -4,7 +4,11 @@ drop table system;
drop table oidc;
drop table users;
drop table user_addresses;
drop table teams;
drop table user_teams;
drop table channels;
drop table tools;
drop table tasks;
drop table history;
drop table files;
drop table share;
@@ -1,36 +1,46 @@
create table providers (
id text not null primary key,
name text,
team_id text not null,
name text not null,
key text,
url text,
_version 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,
provider_id text,
name text,
modelname text,
configuration text,
_version text
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 (
chat_model_id text,
default_lang text,
register_enabled numeric,
password_enabled numeric,
_version text
id text not null primary key,
default_lang text not null default 'en',
register_enabled numeric not null default 1,
password_enabled numeric not null default 1,
_date_created numeric not null default current_timestamp,
_date_updated numeric,
_version text not null
);
create table oidc (
id text not null primary key,
label text,
domain text,
client_id text,
client_secret text,
wellknown_url text,
_version text
label text not null,
domain text not null,
client_id text not null,
client_secret text not null,
wellknown_url text not null,
_date_created numeric not null default current_timestamp,
_date_updated numeric,
_version text not null
);
create table users (
@@ -41,55 +51,112 @@ create table users (
email text not null unique,
picture text not null default '',
enabled numeric default true,
administrator numeric default false,
theme text default 'auto',
lang text default 'en',
_date_created numeric not null default current_timestamp,
_date_updated numeric,
_version text not null
);
create table user_addresses (
id text not null primary key,
user_id text,
type text,
address text,
_version text
user_id text not null,
type text not null,
address text not null,
_date_created numeric not null default current_timestamp,
_date_updated numeric,
_version text not null
);
create table teams (
id text not null primary key,
label text not null,
default_model_id text,
_date_created numeric not null default current_timestamp,
_date_updated numeric ,
_version text not null
);
create table user_teams (
id text not null primary key,
user_id text not null,
team_id text not null,
administrator bool default 0,
_date_created numeric not null default current_timestamp,
_date_updated numeric,
_version text not null
);
create table channels (
id text not null primary key,
name text,
type text,
enabled numeric,
configuration text,
_version text
team_id text not null,
name text not null,
type text not null,
enabled numeric not null default 1,
configuration text not null default '{}',
_date_created numeric not null default current_timestamp,
_date_updated numeric,
_version text not null
);
create table tools (
id text not null primary key,
name text,
type text,
enabled numeric,
configuration text,
_version text
team_id text not null,
name text not null,
type text not null,
enabled numeric not null default 1,
configuration text not null default '{}',
_date_created numeric not null default current_timestamp,
_date_updated numeric,
_version text not null
);
create table tasks (
id text not null primary key,
owner_id text,
model_id text,
label text,
prompt text,
cron text,
status text,
team_id text not null,
model_id text not null,
label text not null,
prompt text not null,
cron text not null,
status text not null,
next_datetime numeric,
_version text
_date_created numeric not null default current_timestamp,
_date_updated numeric,
_version text not null
);
create table history (
id text not null primary key,
task_id text,
start_datetimle numeric,
task_id text not null,
start_datetime numeric,
end_datetime numeric,
prompt text,
log text,
response text,
_version text
prompt text not null,
log text not null,
response text not null,
_date_created numeric not null default current_timestamp,
_date_updated numeric,
_version text not null
);
create table files (
id text not null primary key,
name text not null default '',
content_type text not null default '',
storage_path text not null default '',
storage_filename text not null default '',
content blob,
_date_created numeric not null default current_timestamp,
_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
);
@@ -1,2 +1,2 @@
insert into settings (default_lang, register_enabled, password_enabled, _version)
values ('en', true, true, 'init');
insert into settings (id, default_lang, register_enabled, password_enabled, _version)
values ('settings', 'en', true, true, 'init');
@@ -1 +0,0 @@
drop table files;
@@ -1,11 +0,0 @@
create table files (
id text not null primary key,
name text not null default '',
content_type text not null default '',
storage_path text not null default '',
storage_filename text not null default '',
date_created numeric not null default current_timestamp,
date_updated numeric not null default 0,
content blob,
_version text not null
);
@@ -1,45 +1,34 @@
package turso
import (
"github.com/jmoiron/sqlx"
"trankilou.fr/lassistanoque/backend/internal/adapter/database/dberrors"
"database/sql"
"log"
"gitea.trankilou.fr/fabien/lasebuche"
"trankilou.fr/lassistanoque/backend/internal/domain"
"trankilou.fr/lassistanoque/backend/internal/utility"
)
type TursoSettingsRepository struct {
DB *sqlx.DB
db *sql.DB
SettingsTable lasebuche.Table[domain.Settings]
}
func NewTursoSettingsRepository(db *sql.DB) *TursoSettingsRepository {
dialect := lasebuche.NewSqliteDialect()
settingsTable, err := lasebuche.NewTable[domain.Settings](db, dialect, "settings")
if err != nil {
log.Fatalf("error creating lasebuche team table")
}
return &TursoSettingsRepository{
db: db,
SettingsTable: settingsTable,
}
}
func (sr *TursoSettingsRepository) GetSettings() (*domain.Settings, error) {
var settings domain.Settings
err := sr.DB.Get(&settings, "select * from settings limit 1")
return &settings, err
return sr.SettingsTable.Get("settings")
}
func (sr *TursoSettingsRepository) UpdateSettings(setting *domain.Settings) (*domain.Settings, error) {
newVersion := utility.GenID()
res, err := sr.DB.Exec(
`update settings
set chat_model_id=$1,
default_lang=$2,
register_enabled=$3,
password_enabled=$4,
_version=:$5
where _version=$6`,
setting.ChatModelID, setting.DefaultLang, setting.RegisterEnabled,
setting.PasswordEnabled, newVersion, setting.VersionId,
)
if err != nil {
return nil, err
}
if count, _ := res.RowsAffected(); count == 0 {
return nil, dberrors.ErrNoRowUpdated
}
setting.VersionId = newVersion
return setting, nil
return sr.SettingsTable.Update(setting)
}
@@ -1,90 +1,143 @@
package turso
import (
"github.com/jmoiron/sqlx"
"trankilou.fr/lassistanoque/backend/internal/adapter/database/dberrors"
"database/sql"
"log"
"trankilou.fr/lassistanoque/backend/internal/domain"
"trankilou.fr/lassistanoque/backend/internal/utility"
"gitea.trankilou.fr/fabien/lasebuche"
)
type TursoUserRepository struct {
DB *sqlx.DB
DB *sql.DB
UserTable lasebuche.Table[domain.User]
TeamTable lasebuche.Table[domain.Team]
UserTeamTable lasebuche.Table[domain.UserTeam]
AddressTable lasebuche.Table[domain.UserAddress]
}
func NewTursoUserRepository(db *sql.DB) *TursoUserRepository {
dialect := lasebuche.NewSqliteDialect()
userTable, err := lasebuche.NewTable[domain.User](db, dialect, "users")
if err != nil {
log.Fatalf("error creating lasebuche user table")
}
teamTable, err := lasebuche.NewTable[domain.Team](db, dialect, "teams")
if err != nil {
log.Fatalf("error creating lasebuche team table")
}
userTeamTable, err := lasebuche.NewTable[domain.UserTeam](db, dialect, "user_teams")
if err != nil {
log.Fatalf("error creating lasebuche team table")
}
userAddressTable, err := lasebuche.NewTable[domain.UserAddress](db, dialect, "user_addresses")
if err != nil {
log.Fatalf("error creating lasebuche team table")
}
return &TursoUserRepository{
DB: db,
UserTable: userTable,
TeamTable: teamTable,
UserTeamTable: userTeamTable,
AddressTable: userAddressTable,
}
}
func (ur *TursoUserRepository) FindUser(id string) (*domain.User, error) {
var user domain.User
err := ur.DB.Get(&user, "select * from users where id=$1", id)
return &user, err
return ur.UserTable.Get(id)
}
func (ur *TursoUserRepository) FindUserByEmail(email string) (*domain.User, error) {
var user domain.User
err := ur.DB.Get(&user, "select * from users where email=$1", email)
return &user, err
}
func (ur *TursoUserRepository) CountUsers() (int, error) {
var count int
err := ur.DB.Get(&count, "select count(*) from users")
return count, err
return ur.UserTable.SelectOne("email=$1", email)
}
func (ur *TursoUserRepository) ListUsers() ([]*domain.User, error) {
var users []*domain.User
err := ur.DB.Select(&users, "select * from users order by lastname, firstname")
return users, err
return ur.UserTable.SelectWhere("")
}
func (ur *TursoUserRepository) CreateUser(user *domain.User) (*domain.User, error) {
user.ID = utility.GenID()
user.VersionId = utility.GenID()
_, err := ur.DB.NamedExec(
`insert into users (id, email, firstname, lastname, enabled, password, administrator, _version)
values (:id, :email, :firstname, :lastname, :enabled, :password, :administrator, :_version)`,
user)
if err != nil {
return nil, err
}
return user, nil
return ur.UserTable.Insert(user)
}
func (ur *TursoUserRepository) UpdateUser(user *domain.User) (*domain.User, error) {
newVersion := utility.GenID()
res, err := ur.DB.Exec(
`update users
set email=$1,
firstname=$2,
lastname=$3,
enabled=$4,
administrator=$5
_version=:$6
where id=$7 and _version=$8`,
user.Email, user.Firstname, user.Lastname,
user.Enabled, user.Administrator,
newVersion, user.ID, user.VersionId,
)
if err != nil {
return nil, err
}
if count, _ := res.RowsAffected(); count == 0 {
return nil, dberrors.ErrNoRowUpdated
}
user.VersionId = newVersion
return user, nil
return ur.UserTable.Update(user)
}
func (ur *TursoUserRepository) DeleteUser(id string) error {
res, err := ur.DB.Exec("delete from users where id=$1", id)
if count, _ := res.RowsAffected(); count == 0 {
return dberrors.ErrNoRowUpdated
}
return err
return ur.UserTable.Delete(id)
}
func (ur *TursoUserRepository) FindTeam(userid string, teamid string) (*domain.Team, error) {
return ur.TeamTable.SelectOne("id=$1 and user_id=$2", teamid, userid)
}
func (ur *TursoUserRepository) ListTeams(userid string) ([]*domain.Team, error) {
return ur.TeamTable.SelectWhere("user_id=$1", userid)
}
func (ur *TursoUserRepository) CreateTeam(userid string, team *domain.Team) (*domain.Team, error) {
// TODO : gérér dans une transaction
// TODO : déplacer dans le service
team, err := ur.TeamTable.Insert(team)
if err != nil {
return nil, err
}
userTeam := &domain.UserTeam{
UserID: userid,
TeamID: team.ID,
Administrator: true,
}
_, err = ur.UserTeamTable.Insert(userTeam)
if err != nil {
return nil, err
}
return team, nil
}
func (ur *TursoUserRepository) UpdateTeam(userid string, team *domain.Team) (*domain.Team, error) {
return ur.TeamTable.Update(team)
}
func (ur *TursoUserRepository) DeleteTeam(userid string, teamid string) error {
return ur.TeamTable.Delete(teamid)
}
func (ur *TursoUserRepository) FindUserTeam(userid string, teamid string) (*domain.UserTeam, error) {
return ur.UserTeamTable.SelectOne("team_id=$1 and user_id=$2", teamid, userid)
}
func (ur *TursoUserRepository) ListUserTeams(userid string) ([]*domain.UserTeam, error) {
return ur.UserTeamTable.SelectWhere("user_id=$1", userid)
}
func (ur *TursoUserRepository) CreateUserTeam(userid string, team *domain.UserTeam) (*domain.UserTeam, error) {
return ur.UserTeamTable.Insert(team)
}
func (ur *TursoUserRepository) UpdateUserTeam(userid string, team *domain.UserTeam) (*domain.UserTeam, error) {
return ur.UserTeamTable.Update(team)
}
func (ur *TursoUserRepository) DeleteUserTeam(userid string, teamid string) error {
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)
}
func (ur *TursoUserRepository) CreateUserAddress(userid string, addr *domain.UserAddress) (*domain.UserAddress, error) {
addr.UserID = userid
return ur.AddressTable.Insert(addr)
}
func (ur *TursoUserRepository) UpdateUserAddress(userid string, addr *domain.UserAddress) (*domain.UserAddress, error) {
return ur.AddressTable.Update(addr)
}
func (ur *TursoUserRepository) DeleteUserAddress(userid string, addrID string) error {
return ur.AddressTable.DeleteWhere("id=$1 and user_id=$2", addrID, userid)
}
+12 -16
View File
@@ -15,11 +15,10 @@ import (
// Claims personnalisées embarquées dans le JWT.
type Claims struct {
UserID string `json:"uid"`
Email string `json:"email"`
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
Administrator bool `json:"administrator"`
UserID string `json:"uid"`
Email string `json:"email"`
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
jwt.RegisteredClaims
}
@@ -45,11 +44,10 @@ func NewJwtTokenManager(accessTTL, refreshTTL time.Duration, issuer string) *Jwt
func (tm *JwtTokenManager) GenerateAccessToken(user *domain.User) (string, time.Time, error) {
expiresAt := time.Now().Add(tm.accessTTL)
claims := Claims{
UserID: user.ID,
Email: user.Email,
Firstname: user.Firstname,
Lastname: user.Lastname,
Administrator: user.Administrator,
UserID: user.ID,
Email: user.Email,
Firstname: user.Firstname,
Lastname: user.Lastname,
RegisteredClaims: jwt.RegisteredClaims{
Issuer: tm.issuer,
Subject: user.ID,
@@ -93,11 +91,10 @@ func (tm *JwtTokenManager) ParseAndValidate(tokenString string) (*domain.User, e
return nil, errors.New("token invalide")
}
return &domain.User{
ID: claims.UserID,
Email: claims.Email,
Firstname: claims.Firstname,
Lastname: claims.Lastname,
Administrator: claims.Administrator,
ID: claims.UserID,
Email: claims.Email,
Firstname: claims.Firstname,
Lastname: claims.Lastname,
}, nil
}
@@ -122,7 +119,6 @@ func (tm *JwtTokenManager) TokenMiddleware(next echo.HandlerFunc) echo.HandlerFu
c.Set(auth.ContextUserIDKey, claims.ID)
c.Set(auth.ContextEmailKey, claims.Email)
c.Set(auth.ContextNameKey, claims.Firstname+" "+claims.Lastname)
c.Set(auth.ContextAdminKey, claims.Administrator)
return next(c)
}
+8 -6
View File
@@ -1,13 +1,15 @@
package domain
import "database/sql"
import "time"
type Settings struct {
ChatModelID sql.NullString `db:"chat_model_id"`
DefaultLang string `db:"default_lang"`
RegisterEnabled bool `db:"register_enabled"`
PasswordEnabled bool `db:"password_enabled"`
VersionId string `db:"_version"`
ID string `db:"id" json:"id"`
DefaultLang string `db:"default_lang"`
RegisterEnabled bool `db:"register_enabled"`
PasswordEnabled bool `db:"password_enabled"`
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 SettingsRepository interface {
+74 -10
View File
@@ -1,23 +1,87 @@
package domain
import (
"time"
)
type User struct {
ID string `db:"id" json:"id"`
Firstname string `db:"firstname" json:"firstname,omitempty"`
Lastname string `db:"lastname" json:"lastname,omitempty"`
Password string `db:"password" json:"-"`
Email string `db:"email" json:"email,omitempty"`
PictureID string `db:"picture" json:"picture,omitempty"`
Enabled bool `db:"enabled" json:"enabled"`
Administrator bool `db:"administrator" json:"administrator"`
VersionId string `db:"_version" json:"-"`
ID string `db:"id" json:"id"`
Firstname string `db:"firstname" json:"firstname,omitempty"`
Lastname string `db:"lastname" json:"lastname,omitempty"`
Password string `db:"password" json:"-"`
Email string `db:"email" json:"email,omitempty"`
PictureID string `db:"picture" json:"picture,omitempty"`
Enabled bool `db:"enabled" json:"enabled"`
Theme string `db:"theme" json:"theme"`
Lang string `db:"lang" json:"lang"`
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 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"`
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"`
Administrator bool `db:"administrator" json:"administrator"`
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 UserAddress struct {
ID string `db:"id" json:"id"`
UserID string `db:"user_id" json:"user_id"`
Type string `db:"type" json:"type"`
Address string `db:"address_id" json:"address"`
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 TeamRelation struct {
IsAdministrator bool `json:"administrator"`
Team *Team `json:"team"`
}
type UserWithTeams struct {
User *User `json:"user"`
Teams []*TeamRelation `json:"teams"`
}
type UserRepository interface {
CountUsers() (int, error)
FindUser(id string) (*User, error)
FindUserByEmail(email string) (*User, error)
ListUsers() ([]*User, error)
CreateUser(user *User) (*User, error)
UpdateUser(user *User) (*User, error)
DeleteUser(id string) error
FindTeam(userid string, teamid string) (*Team, error)
ListTeams(userid string) ([]*Team, error)
CreateTeam(userid string, team *Team) (*Team, error)
UpdateTeam(userid string, team *Team) (*Team, error)
DeleteTeam(userid string, teamid string) error
FindUserTeam(userid string, teamid string) (*UserTeam, error)
ListUserTeams(userid string) ([]*UserTeam, error)
CreateUserTeam(userid string, team *UserTeam) (*UserTeam, error)
UpdateUserTeam(userid string, team *UserTeam) (*UserTeam, error)
DeleteUserTeam(userid string, teamid string) error
PopulateUserWithTeams(user *User) (*UserWithTeams, error)
ListUserAddresses(userid string) ([]*UserAddress, error)
CreateUserAddress(userid string, addr *UserAddress) (*UserAddress, error)
UpdateUserAddress(userid string, addr *UserAddress) (*UserAddress, error)
DeleteUserAddress(userid string, addrID string) error
}
+26 -2
View File
@@ -4,6 +4,7 @@ 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/user"
)
@@ -15,7 +16,7 @@ func NewUserGroup(prefix string, e *echo.Group, service *user.Service, middlewar
_ = userHandler
auth := e.Group(prefix, middlewares...)
auth.GET("/me", userHandler.Me)
// auth.GET("/register", userHandler.Register)
auth.PUT("/:id", userHandler.Update)
return auth
}
@@ -27,7 +28,30 @@ func (h UserHandler) Me(c *echo.Context) error {
userID := c.Get(auth.ContextUserIDKey).(string)
user, err := h.userService.GetUser(userID)
if err != nil {
return echo.NewHTTPError(http.StatusForbidden, err.Error())
return echo.NewHTTPError(http.StatusUnauthorized, err.Error())
}
return c.JSON(http.StatusOK, user)
}
func (h UserHandler) Update(c *echo.Context) error {
userID := c.Get(auth.ContextUserIDKey).(string)
id := c.Param("id")
var updUser domain.User
if err := c.Bind(&updUser); err != nil {
return c.String(http.StatusBadRequest, "bad request")
}
updUser.ID = id
isAdmin := false // TODO : check if caller is admin
if !(isAdmin || userID == id) {
return echo.NewHTTPError(http.StatusForbidden, "unauthorized to update user")
}
user, err := h.userService.UpdateUser(&updUser, isAdmin)
if err != nil {
return c.String(http.StatusBadRequest, err.Error())
}
return c.JSON(http.StatusOK, user)
}
@@ -33,7 +33,6 @@ type Session struct {
const ContextUserIDKey = "userID"
const ContextEmailKey = "userEmail"
const ContextNameKey = "userName"
const ContextAdminKey = "admin"
// Interface définie ici car c'est un port propre au cas d'usage "auth"
type Authenticator interface {
+19
View File
@@ -26,3 +26,22 @@ func (s *Service) GetUser(id string) (*domain.User, error) {
}
return user, nil
}
func (s *Service) UpdateUser(updUser *domain.User, fromAdmin bool) (*domain.User, error) {
user, err := s.repo.FindUser(updUser.ID)
if err != nil {
return nil, err
}
if fromAdmin {
user.Enabled = updUser.Enabled
}
user.Email = updUser.Email
user.Firstname = updUser.Firstname
user.Lastname = updUser.Lastname
user.Theme = updUser.Theme
user.Lang = updUser.Lang
return s.repo.UpdateUser(user)
}
Binary file not shown.