forked from phc/website
First table is working
This commit is contained in:
@@ -329,7 +329,7 @@ const TabellaApprovazioni = ({ pendingApprovazioni }) => {
|
|||||||
return (
|
return (
|
||||||
<div class="table approvazioni">
|
<div class="table approvazioni">
|
||||||
<div class="download header"></div>
|
<div class="download header"></div>
|
||||||
<div class="hash header">PDF (Hash)</div>
|
<div class="hash header">PDF</div>
|
||||||
<div class="title header">Dispensa</div>
|
<div class="title header">Dispensa</div>
|
||||||
<div class="owner header">Proprietario</div>
|
<div class="owner header">Proprietario</div>
|
||||||
<div class="actions header">Azioni</div>
|
<div class="actions header">Azioni</div>
|
||||||
@@ -347,7 +347,7 @@ const TabellaApprovazioni = ({ pendingApprovazioni }) => {
|
|||||||
</div>
|
</div>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<a href={`/appunti/${id}`} title={id}>
|
<a href={`/appunti/${id}`} title={id}>
|
||||||
<i class="fa-solid fa-book"></i> {title}
|
{title}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="owner">
|
<div class="owner">
|
||||||
@@ -413,9 +413,7 @@ const App = ({}) => {
|
|||||||
<div class="edit-container">
|
<div class="edit-container">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<a href="/appunti/6f82dca3d83b475c">
|
<a href="/appunti/6f82dca3d83b475c">
|
||||||
<span class="title">
|
<span class="title">Mezzedimi</span>
|
||||||
<i class="fa-solid fa-book"></i> Mezzedimi
|
|
||||||
</span>
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="form">
|
<div class="form">
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{{template "base" .}}
|
||||||
|
|
||||||
|
{{define "title"}}{{ .Dispensa.Title }} • Appunti • PHC{{end}}
|
||||||
|
|
||||||
|
{{define "body"}}
|
||||||
|
<section>
|
||||||
|
<h1>
|
||||||
|
<i class="fas fa-book"></i>
|
||||||
|
{{ .Dispensa.Title }}
|
||||||
|
</h1>
|
||||||
|
<p>
|
||||||
|
{{ .Dispensa.Description }}
|
||||||
|
</p>
|
||||||
|
{{end}}
|
||||||
+38
-3
@@ -2,16 +2,51 @@ package appunti
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"git.phc.dm.unipi.it/phc/website/database"
|
"git.phc.dm.unipi.it/phc/website/database"
|
||||||
|
"git.phc.dm.unipi.it/phc/website/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Service isola l'handler dal modulo del database e si occupa solo delle interazioni riguardanti gli appunti e le dispense
|
||||||
type Service interface {
|
type Service interface {
|
||||||
ViewDispense() error
|
GetDispensa(id string) (*model.Dispensa, error)
|
||||||
|
CreateDispensa(template model.Dispensa) (string, error)
|
||||||
|
SetDispensaTags(dispensaId string, tags []string) error
|
||||||
|
// GetDispensaTags(dispensaId string) ([]string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefaultService struct {
|
type DefaultService struct {
|
||||||
DB database.DB
|
DB *database.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultService) CreateDispensa(user string, template database.Dispensa) {
|
var _ Service = &DefaultService{}
|
||||||
|
|
||||||
|
func (s *DefaultService) GetDispensa(id string) (*model.Dispensa, error) {
|
||||||
|
dispensa, err := s.DB.GetDispensa(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tags, err := s.DB.GetTags(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &model.Dispensa{
|
||||||
|
Id: dispensa.Id,
|
||||||
|
OwnerId: dispensa.OwnerId,
|
||||||
|
Title: dispensa.Title,
|
||||||
|
Description: dispensa.Description,
|
||||||
|
Tags: tags,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultService) CreateDispensa(template model.Dispensa) (string, error) {
|
||||||
|
return s.DB.CreateDispensa(database.Dispensa{
|
||||||
|
OwnerId: template.OwnerId,
|
||||||
|
Title: template.Title,
|
||||||
|
Description: template.Description,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultService) SetDispensaTags(dispensaId string, tags []string) error {
|
||||||
|
return s.DB.SetTags(dispensaId, tags)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,28 +3,32 @@ package main
|
|||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"git.phc.dm.unipi.it/phc/website/appunti"
|
||||||
"git.phc.dm.unipi.it/phc/website/articles"
|
"git.phc.dm.unipi.it/phc/website/articles"
|
||||||
"git.phc.dm.unipi.it/phc/website/auth"
|
"git.phc.dm.unipi.it/phc/website/auth"
|
||||||
"git.phc.dm.unipi.it/phc/website/config"
|
"git.phc.dm.unipi.it/phc/website/config"
|
||||||
|
"git.phc.dm.unipi.it/phc/website/database"
|
||||||
"git.phc.dm.unipi.it/phc/website/handler"
|
"git.phc.dm.unipi.it/phc/website/handler"
|
||||||
"git.phc.dm.unipi.it/phc/website/lista_utenti"
|
"git.phc.dm.unipi.it/phc/website/lista_utenti"
|
||||||
"git.phc.dm.unipi.it/phc/website/server"
|
"git.phc.dm.unipi.it/phc/website/server"
|
||||||
"git.phc.dm.unipi.it/phc/website/storia"
|
"git.phc.dm.unipi.it/phc/website/storia"
|
||||||
"git.phc.dm.unipi.it/phc/website/templates"
|
"git.phc.dm.unipi.it/phc/website/templates"
|
||||||
|
"git.phc.dm.unipi.it/phc/website/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
config.Load()
|
config.Load()
|
||||||
|
|
||||||
authService := auth.NewDefaultService(config.AuthServiceHost)
|
auth := auth.NewDefaultService(config.AuthServiceHost)
|
||||||
|
|
||||||
listaUtentiService, err := lista_utenti.New(authService, config.ListaUtenti)
|
// Create database connection and apply pending migrations
|
||||||
if err != nil {
|
db := util.Must(database.NewSqlite3Database("phc-server.local.db"))
|
||||||
log.Fatal(err)
|
if err := db.Migrate("./database/migrations"); err != nil {
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
h := &handler.DefaultHandler{
|
app := server.NewFiberServer(&handler.DefaultHandler{
|
||||||
AuthService: authService,
|
AuthService: auth,
|
||||||
Renderer: templates.NewRenderer(
|
Renderer: templates.NewRenderer(
|
||||||
"./_views/",
|
"./_views/",
|
||||||
"./_views/base.html",
|
"./_views/base.html",
|
||||||
@@ -35,13 +39,14 @@ func main() {
|
|||||||
Storia: &storia.JsonFileStoria{
|
Storia: &storia.JsonFileStoria{
|
||||||
Path: "./_content/storia.yaml",
|
Path: "./_content/storia.yaml",
|
||||||
},
|
},
|
||||||
ListaUtenti: listaUtentiService,
|
ListaUtenti: util.Must(lista_utenti.New(auth, config.ListaUtenti)),
|
||||||
}
|
AppuntiService: &appunti.DefaultService{
|
||||||
|
DB: db,
|
||||||
app := server.NewFiberServer(h)
|
},
|
||||||
|
})
|
||||||
|
|
||||||
log.Printf("Starting server on host %q", config.Host)
|
log.Printf("Starting server on host %q", config.Host)
|
||||||
if err := app.Listen(config.Host); err != nil {
|
if err := app.Listen(config.Host); err != nil {
|
||||||
log.Fatal(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-59
@@ -1,15 +1,11 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
|
|
||||||
type DBMigrate interface {
|
type DBMigrate interface {
|
||||||
Migrate(migrationDir string) error
|
Migrate(migrationDir string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entities
|
|
||||||
|
|
||||||
type DBDispense interface {
|
type DBDispense interface {
|
||||||
CreateDispensa(template Dispensa) (string, error)
|
CreateDispensa(template Dispensa) (string, error)
|
||||||
GetDispensa(id string) (Dispensa, error)
|
GetDispensa(id string) (Dispensa, error)
|
||||||
@@ -18,74 +14,34 @@ type DBDispense interface {
|
|||||||
DeleteDispensa(id string) error
|
DeleteDispensa(id string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type DBUpload interface {
|
type DBUploads interface {
|
||||||
CreateUpload(template UploadedContent) (string, error)
|
CreateUpload(template Upload) (string, error)
|
||||||
DeleteUpload(id UploadedContent) (UploadedContent, error)
|
GetUpload(id string) (Upload, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DBHashApprovals interface {
|
type DBFileApprovals interface {
|
||||||
CreateApprovedHash(a HashApproval) error
|
CreateFileApproval(template FileApproval) (string, error)
|
||||||
GetApprovedHash(hash string) (HashApproval, error)
|
GetFileApproval(id string) (FileApproval, error)
|
||||||
AllApprovedHash(a HashApproval) error
|
AllFileApprovals() ([]FileApproval, error)
|
||||||
|
UpdateFileApproval(d FileApproval) error
|
||||||
|
DeleteFileApproval(id string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type DBHashRejections interface {
|
type DBDownloads interface {
|
||||||
CreateRejectedHash(a HashRejection) error
|
CreateDownload(template Download) (string, error)
|
||||||
GetRejectedHash(hash string) (HashRejection, error)
|
|
||||||
AllRejectedHash(a HashRejection) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Relations
|
|
||||||
|
|
||||||
type DBTags interface {
|
type DBTags interface {
|
||||||
SetTags(dispensaId string, tag []string) error
|
SetTags(dispensaId string, tags []string) error
|
||||||
GetTags(dispensaId string) ([]string, error)
|
GetTags(dispensaId string) ([]string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DBOwners interface {
|
|
||||||
CreateOwner(owner, owned string) error
|
|
||||||
GetOwner(ownedId string) (string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type DBAuthors interface {
|
|
||||||
CreateAuthor(userId, dispensaId string) error
|
|
||||||
GetAuthorId(dispensaId string) (string, error)
|
|
||||||
GetUserDispenseIds(user string) ([]string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type DBCreationTimes interface {
|
|
||||||
CreateCreationTime(entityId string, createdAt time.Time) error
|
|
||||||
GetCreationTime(entityId string) (time.Time, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type DBOther interface {
|
|
||||||
// AllDispenseOfUser ritorna tutte le dispense di un certo utente
|
|
||||||
AllDispenseOfUser(username string) ([]Dispensa, error)
|
|
||||||
// AllLatestsApprovedDispenseUploads ritorna una lista contenente tutte le dispense con almeno un upload approvato e ritorna la coppia della (dispensa, upload più recente)
|
|
||||||
AllLatestsApprovedDispenseUploads() ([]struct {
|
|
||||||
Dispensa
|
|
||||||
UploadedContent
|
|
||||||
}, error)
|
|
||||||
// AllDispenseWithState ...
|
|
||||||
AllDispenseWithState(username string) ([]struct {
|
|
||||||
Dispensa
|
|
||||||
State string
|
|
||||||
}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DB main "interface group" for interacting with the database, with this technique we can test each "table" api of the DB in isolation.
|
// DB main "interface group" for interacting with the database, with this technique we can test each "table" api of the DB in isolation.
|
||||||
type DB struct {
|
type DB struct {
|
||||||
DBMigrate
|
DBMigrate
|
||||||
|
|
||||||
DBDispense
|
DBDispense
|
||||||
DBUpload
|
DBUploads
|
||||||
DBHashApprovals
|
DBFileApprovals
|
||||||
DBHashRejections
|
DBDownloads
|
||||||
|
|
||||||
DBTags
|
DBTags
|
||||||
DBOwners
|
|
||||||
DBAuthors
|
|
||||||
DBCreationTimes
|
|
||||||
|
|
||||||
DBOther
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
|
|
||||||
--
|
|
||||||
-- Entities
|
|
||||||
--
|
|
||||||
|
|
||||||
-- Dispense
|
-- Dispense
|
||||||
CREATE TABLE IF NOT EXISTS "dispense"(
|
CREATE TABLE IF NOT EXISTS "dispense"(
|
||||||
"id" TEXT NOT NULL PRIMARY KEY,
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
@@ -22,7 +18,7 @@ CREATE TABLE IF NOT EXISTS "uploads"(
|
|||||||
FOREIGN KEY (dispensa_id) REFERENCES dispense(id)
|
FOREIGN KEY (dispensa_id) REFERENCES dispense(id)
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Contenuti con hash approvati
|
-- Approvazioni contenuti caricati
|
||||||
CREATE TABLE IF NOT EXISTS "file_approvals"(
|
CREATE TABLE IF NOT EXISTS "file_approvals"(
|
||||||
"id" TEXT NOT NULL PRIMARY KEY,
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
"created_at" TEXT NOT NULL,
|
"created_at" TEXT NOT NULL,
|
||||||
@@ -37,11 +33,7 @@ CREATE TABLE IF NOT EXISTS "downloads"(
|
|||||||
"dispensa_id" TEXT NOT NULL,
|
"dispensa_id" TEXT NOT NULL,
|
||||||
"timestamp" TEXT NOT NULL,
|
"timestamp" TEXT NOT NULL,
|
||||||
FOREIGN KEY (dispensa_id) REFERENCES dispense(id)
|
FOREIGN KEY (dispensa_id) REFERENCES dispense(id)
|
||||||
)
|
);
|
||||||
|
|
||||||
--
|
|
||||||
-- Relations
|
|
||||||
--
|
|
||||||
|
|
||||||
-- Tags per le dispense
|
-- Tags per le dispense
|
||||||
CREATE TABLE IF NOT EXISTS "tags"(
|
CREATE TABLE IF NOT EXISTS "tags"(
|
||||||
|
|||||||
+23
-32
@@ -2,43 +2,34 @@ package database
|
|||||||
|
|
||||||
type Dispensa struct {
|
type Dispensa struct {
|
||||||
Id string `db:"id"`
|
Id string `db:"id"`
|
||||||
|
CreatedAt string `db:"created_at"`
|
||||||
|
OwnerId string `db:"owner_id"`
|
||||||
Title string `db:"title"`
|
Title string `db:"title"`
|
||||||
Description string `db:"description"`
|
Description string `db:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Upload struct {
|
||||||
|
Id string `db:"id"`
|
||||||
|
CreatedAt string `db:"created_at"`
|
||||||
|
OwnerId string `db:"owner_id"`
|
||||||
|
DispensaId string `db:"dispensa_id"`
|
||||||
|
File string `db:"file"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileApproval struct {
|
||||||
|
Id string `db:"id"`
|
||||||
|
CreatedAt string `db:"created_at"`
|
||||||
|
OwnerId string `db:"owner_id"`
|
||||||
|
UploadId string `db:"upload_id"`
|
||||||
|
Status string `db:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Download struct {
|
||||||
|
DispensaId string `db:"dispensa_id"`
|
||||||
|
Timestamp string `db:"timestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
type Tag struct {
|
type Tag struct {
|
||||||
DispensaId string `db:"dispensa_id"`
|
DispensaId string `db:"dispensa_id"`
|
||||||
Tag string `db:"tag"`
|
Tag string `db:"tag"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UploadedContent struct {
|
|
||||||
Id string `db:"id"`
|
|
||||||
Hash string `db:"hash"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type HashApproval struct {
|
|
||||||
Id string `db:"id"`
|
|
||||||
Hash string `db:"hash"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type HashRejection struct {
|
|
||||||
Id string `db:"id"`
|
|
||||||
Hash string `db:"hash"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Relations
|
|
||||||
|
|
||||||
type Owner struct {
|
|
||||||
OwnerId string `db:"owner_id"`
|
|
||||||
OwnedId string `db:"owned_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Author struct {
|
|
||||||
UserId string `db:"user_id"`
|
|
||||||
DispensaId string `db:"dispensa_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CreationTime struct {
|
|
||||||
EntityId string `db:"entity_id"`
|
|
||||||
CreatedAt string `db:"created_at"`
|
|
||||||
}
|
|
||||||
|
|||||||
+40
-14
@@ -22,10 +22,12 @@ type sqliteDB struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (db *sqliteDB) Migrate(migrationFolder string) error {
|
func (db *sqliteDB) Migrate(migrationFolder string) error {
|
||||||
|
log.Printf(`Creating migrations table`)
|
||||||
if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS migrations(timestamp TEXT, filename TEXT)`); err != nil {
|
if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS migrations(timestamp TEXT, filename TEXT)`); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf(`Loading applied migrations`)
|
||||||
appliedMigrations := []migration{}
|
appliedMigrations := []migration{}
|
||||||
if err := db.Select(&appliedMigrations, `SELECT * FROM migrations`); err != nil {
|
if err := db.Select(&appliedMigrations, `SELECT * FROM migrations`); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -86,13 +88,14 @@ func (db *sqliteDB) Migrate(migrationFolder string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (db *sqliteDB) CreateDispensa(template Dispensa) (string, error) {
|
func (db *sqliteDB) CreateDispensa(template Dispensa) (string, error) {
|
||||||
template.Id = "dispensa/" + util.GenerateRandomString(15)
|
template.Id = "dispensa/" + util.GenerateRandomString(8)
|
||||||
|
template.CreatedAt = time.Now().Format(time.RFC3339)
|
||||||
|
|
||||||
if _, err := db.NamedExec(`
|
if _, err := db.NamedExec(`
|
||||||
INSERT INTO
|
INSERT INTO
|
||||||
dispense(id, title, description)
|
dispense(id, created_at, owner_id, title, description)
|
||||||
VALUES
|
VALUES
|
||||||
(:id, :title, :description)
|
(:id, :created_at, :owner_id, :title, :description)
|
||||||
`, &template); err != nil {
|
`, &template); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -102,7 +105,6 @@ func (db *sqliteDB) CreateDispensa(template Dispensa) (string, error) {
|
|||||||
|
|
||||||
func (db *sqliteDB) GetDispensa(id string) (Dispensa, error) {
|
func (db *sqliteDB) GetDispensa(id string) (Dispensa, error) {
|
||||||
var dispensa Dispensa
|
var dispensa Dispensa
|
||||||
|
|
||||||
if err := db.Get(&dispensa, `SELECT * FROM dispense WHERE id = ?`, id); err != nil {
|
if err := db.Get(&dispensa, `SELECT * FROM dispense WHERE id = ?`, id); err != nil {
|
||||||
return Dispensa{}, err
|
return Dispensa{}, err
|
||||||
}
|
}
|
||||||
@@ -112,7 +114,6 @@ func (db *sqliteDB) GetDispensa(id string) (Dispensa, error) {
|
|||||||
|
|
||||||
func (db *sqliteDB) AllDispense() ([]Dispensa, error) {
|
func (db *sqliteDB) AllDispense() ([]Dispensa, error) {
|
||||||
var dispense []Dispensa
|
var dispense []Dispensa
|
||||||
|
|
||||||
if err := db.Select(&dispense, `SELECT * FROM dispense`); err != nil {
|
if err := db.Select(&dispense, `SELECT * FROM dispense`); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -122,10 +123,14 @@ func (db *sqliteDB) AllDispense() ([]Dispensa, error) {
|
|||||||
|
|
||||||
func (db *sqliteDB) UpdateDispensa(d Dispensa) error {
|
func (db *sqliteDB) UpdateDispensa(d Dispensa) error {
|
||||||
if _, err := db.NamedExec(`
|
if _, err := db.NamedExec(`
|
||||||
UPDATE dispense
|
UPDATE
|
||||||
SET title = :title,
|
dispense
|
||||||
description = :description
|
SET
|
||||||
WHERE id = :id
|
owner_id = :owner_id
|
||||||
|
title = :title,
|
||||||
|
description = :description
|
||||||
|
WHERE
|
||||||
|
id = :id
|
||||||
`, &d); err != nil {
|
`, &d); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -137,8 +142,32 @@ func (db *sqliteDB) DeleteDispensa(id string) error {
|
|||||||
panic("TODO: Not implemented")
|
panic("TODO: Not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *sqliteDB) CreateUpload(template Upload) (string, error) {
|
||||||
|
template.Id = "upload/" + util.GenerateRandomString(8)
|
||||||
|
|
||||||
|
if _, err := db.NamedExec(`
|
||||||
|
INSERT INTO
|
||||||
|
uploads(id, created_at, owner_id, dispensa_id, file)
|
||||||
|
VALUES
|
||||||
|
(:id, :created_at, :owner_id, :dispensa_id, :file)
|
||||||
|
`, &template); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return template.Id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *sqliteDB) GetUpload(id string) (Upload, error) {
|
||||||
|
var upload Upload
|
||||||
|
if err := db.Select(`SELECT * FROM uploads WHERE id = ?`, id); err != nil {
|
||||||
|
return Upload{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return upload, nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewSqlite3Database(filename string) (*DB, error) {
|
func NewSqlite3Database(filename string) (*DB, error) {
|
||||||
sqlDB, err := sqlx.Open("sqlite3", filename)
|
sqlDB, err := sqlx.Open("sqlite3", filename+"?_fk=1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -153,9 +182,6 @@ func NewSqlite3Database(filename string) (*DB, error) {
|
|||||||
return &DB{
|
return &DB{
|
||||||
DBMigrate: db,
|
DBMigrate: db,
|
||||||
DBDispense: db,
|
DBDispense: db,
|
||||||
// DBUpload: db,
|
DBUploads: db,
|
||||||
// DBApprovedHashes: db,
|
|
||||||
// DBRejectedHashes: db,
|
|
||||||
// DBOther: db,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+79
-9
@@ -5,6 +5,7 @@ import (
|
|||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"git.phc.dm.unipi.it/phc/website/appunti"
|
||||||
"git.phc.dm.unipi.it/phc/website/articles"
|
"git.phc.dm.unipi.it/phc/website/articles"
|
||||||
"git.phc.dm.unipi.it/phc/website/auth"
|
"git.phc.dm.unipi.it/phc/website/auth"
|
||||||
"git.phc.dm.unipi.it/phc/website/lista_utenti"
|
"git.phc.dm.unipi.it/phc/website/lista_utenti"
|
||||||
@@ -16,22 +17,51 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Service interface {
|
type Service interface {
|
||||||
|
//
|
||||||
|
// Pages
|
||||||
|
//
|
||||||
HandleStaticPage(w io.Writer, view string, ctx Context) error
|
HandleStaticPage(w io.Writer, view string, ctx Context) error
|
||||||
HandleUtenti() ([]*model.User, error)
|
|
||||||
HandleListaUtenti() ([]*model.ListUser, error)
|
// Storia
|
||||||
HandleStoriaPage(w io.Writer, ctx Context) error
|
HandleStoriaPage(w io.Writer, ctx Context) error
|
||||||
HandleQueryAppunti(w io.Writer, query string, ctx Context) error
|
|
||||||
|
// Appunti
|
||||||
|
HandleAppuntiPage(w io.Writer, query string, ctx Context) error
|
||||||
HandleAppuntiCondivisiPage(w io.Writer, ctx Context) error
|
HandleAppuntiCondivisiPage(w io.Writer, ctx Context) error
|
||||||
|
HandleDispensaPage(w io.Writer, id string, ctx Context) error
|
||||||
|
|
||||||
|
// News
|
||||||
HandleNewsPage(w io.Writer, ctx Context) error
|
HandleNewsPage(w io.Writer, ctx Context) error
|
||||||
|
|
||||||
|
// Guide
|
||||||
HandleGuidePage(w io.Writer, ctx Context) error
|
HandleGuidePage(w io.Writer, ctx Context) error
|
||||||
|
|
||||||
|
// User
|
||||||
|
HandleProfilePage(w io.Writer, ctx Context) error
|
||||||
|
|
||||||
|
// Article Pages
|
||||||
|
HandleNewsArticlePage(w io.Writer, articleID string, ctx Context) error
|
||||||
|
HandleGuideArticlePage(w io.Writer, articleID string, ctx Context) error
|
||||||
|
|
||||||
|
// RSS
|
||||||
|
HandleNewsFeedPage(w io.Writer) error
|
||||||
|
HandleGuideFeedPage(w io.Writer) error
|
||||||
|
|
||||||
|
//
|
||||||
|
// API
|
||||||
|
//
|
||||||
|
|
||||||
|
// User
|
||||||
HandleLogin(username, password string) (*model.Session, error)
|
HandleLogin(username, password string) (*model.Session, error)
|
||||||
HandleUser(token string) *model.User
|
HandleUser(token string) *model.User
|
||||||
HandleRequiredUser(ctx Context) (*model.User, error)
|
HandleRequiredUser(ctx Context) (*model.User, error)
|
||||||
HandleProfilePage(w io.Writer, ctx Context) error
|
|
||||||
HandleNewsArticlePage(w io.Writer, articleID string, ctx Context) error
|
// User Listing
|
||||||
HandleGuideArticlePage(w io.Writer, articleID string, ctx Context) error
|
HandleUtenti() ([]*model.User, error)
|
||||||
HandleNewsFeedPage(w io.Writer) error
|
HandleListaUtenti() ([]*model.ListUser, error)
|
||||||
HandleGuideFeedPage(w io.Writer) error
|
|
||||||
|
// Appunti
|
||||||
|
HandleCreateDispensa(template model.Dispensa, ctx Context) (*model.Dispensa, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -48,6 +78,7 @@ func (ctx Context) getUser() *model.User {
|
|||||||
// Handler holds references to abstract services for easy testing provided by every module (TODO: Make every field an interface of -Service)
|
// Handler holds references to abstract services for easy testing provided by every module (TODO: Make every field an interface of -Service)
|
||||||
type DefaultHandler struct {
|
type DefaultHandler struct {
|
||||||
AuthService auth.Service
|
AuthService auth.Service
|
||||||
|
AppuntiService appunti.Service
|
||||||
Renderer *templates.TemplateRenderer
|
Renderer *templates.TemplateRenderer
|
||||||
NewsArticlesRegistry *articles.Registry
|
NewsArticlesRegistry *articles.Registry
|
||||||
GuideArticlesRegistry *articles.Registry
|
GuideArticlesRegistry *articles.Registry
|
||||||
@@ -55,6 +86,8 @@ type DefaultHandler struct {
|
|||||||
Storia storia.StoriaService
|
Storia storia.StoriaService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ Service = &DefaultHandler{}
|
||||||
|
|
||||||
func (h *DefaultHandler) HandleStaticPage(w io.Writer, view string, ctx Context) error {
|
func (h *DefaultHandler) HandleStaticPage(w io.Writer, view string, ctx Context) error {
|
||||||
return h.Renderer.Render(w, view, util.Map{
|
return h.Renderer.Render(w, view, util.Map{
|
||||||
"User": ctx.getUser(),
|
"User": ctx.getUser(),
|
||||||
@@ -91,7 +124,7 @@ func (h *DefaultHandler) HandleStoriaPage(w io.Writer, ctx Context) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *DefaultHandler) HandleQueryAppunti(w io.Writer, query string, ctx Context) error {
|
func (h *DefaultHandler) HandleAppuntiPage(w io.Writer, query string, ctx Context) error {
|
||||||
return h.Renderer.Render(w, "appunti.html", util.Map{
|
return h.Renderer.Render(w, "appunti.html", util.Map{
|
||||||
"User": ctx.getUser(),
|
"User": ctx.getUser(),
|
||||||
"Query": query,
|
"Query": query,
|
||||||
@@ -104,6 +137,18 @@ func (h *DefaultHandler) HandleAppuntiCondivisiPage(w io.Writer, ctx Context) er
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *DefaultHandler) HandleDispensaPage(w io.Writer, id string, ctx Context) error {
|
||||||
|
dispensa, err := h.AppuntiService.GetDispensa(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return h.Renderer.Render(w, "dispensa.html", util.Map{
|
||||||
|
"User": ctx.getUser(),
|
||||||
|
"Dispensa": dispensa,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (h *DefaultHandler) HandleNewsPage(w io.Writer, ctx Context) error {
|
func (h *DefaultHandler) HandleNewsPage(w io.Writer, ctx Context) error {
|
||||||
articles, err := h.NewsArticlesRegistry.GetArticles()
|
articles, err := h.NewsArticlesRegistry.GetArticles()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -221,3 +266,28 @@ func (h *DefaultHandler) HandleGuideFeedPage(w io.Writer) error {
|
|||||||
|
|
||||||
return guideFeed.WriteRss(w)
|
return guideFeed.WriteRss(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// API
|
||||||
|
//
|
||||||
|
|
||||||
|
func (h *DefaultHandler) HandleCreateDispensa(template model.Dispensa, ctx Context) (*model.Dispensa, error) {
|
||||||
|
user := ctx.getUser()
|
||||||
|
if user == nil {
|
||||||
|
return nil, ErrNoUser
|
||||||
|
}
|
||||||
|
|
||||||
|
template.OwnerId = user.Username
|
||||||
|
id, err := h.AppuntiService.CreateDispensa(template)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
template.Id = id
|
||||||
|
|
||||||
|
if len(template.Tags) > 0 {
|
||||||
|
h.AppuntiService.SetDispensaTags(id, template.Tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &template, nil
|
||||||
|
}
|
||||||
|
|||||||
+5
-40
@@ -1,44 +1,9 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// Tables
|
|
||||||
|
|
||||||
type Dispensa struct {
|
type Dispensa struct {
|
||||||
// Id is a unique identifier of this object
|
Id string `json:"id"`
|
||||||
Id string
|
OwnerId string `json:"ownerId"`
|
||||||
// CreatedAt is the time of creation of this object
|
Title string `json:"title"`
|
||||||
CreatedAt time.Time
|
Description string `json:"description"`
|
||||||
// Title of this dispensa
|
Tags []string `json:"tags"`
|
||||||
Title string
|
|
||||||
// Description of this dispensa
|
|
||||||
Description string
|
|
||||||
// Tags for this dispensa, used by search to easily categorize dispense
|
|
||||||
Tags string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload represents a file identified by its hash and stored in the appunti content store
|
|
||||||
type Upload struct {
|
|
||||||
// Id is a unique identifier of this object
|
|
||||||
Id string
|
|
||||||
// CreatedAt is the time of creation of this object
|
|
||||||
CreatedAt time.Time
|
|
||||||
// Hash of this file
|
|
||||||
Hash string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApprovedHash represents an approved hash
|
|
||||||
type ApprovedHash struct {
|
|
||||||
// Hash being approved
|
|
||||||
Hash string
|
|
||||||
// CreatedAt is the creation time of this object
|
|
||||||
CreatedAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// RejectedHash represents a rejected hash
|
|
||||||
type RejectedHash struct {
|
|
||||||
// Hash being rejected
|
|
||||||
Hash string
|
|
||||||
// CreatedAt is the creation time of this object
|
|
||||||
CreatedAt time.Time
|
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-1
@@ -115,7 +115,7 @@ func routes(h handler.Service, r fiber.Router) {
|
|||||||
query := c.Query("q", "")
|
query := c.Query("q", "")
|
||||||
|
|
||||||
c.Type("html")
|
c.Type("html")
|
||||||
return h.HandleQueryAppunti(c, query, CreateContext(c))
|
return h.HandleAppuntiPage(c, query, CreateContext(c))
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Get("/appunti/condivisi", func(c *fiber.Ctx) error {
|
r.Get("/appunti/condivisi", func(c *fiber.Ctx) error {
|
||||||
@@ -222,4 +222,18 @@ func routesApi(h handler.Service, r fiber.Router) {
|
|||||||
|
|
||||||
return c.JSON(user)
|
return c.JSON(user)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
r.Post("/appunti", func(c *fiber.Ctx) error {
|
||||||
|
var dispensaTemplate model.Dispensa
|
||||||
|
if err := c.BodyParser(&dispensaTemplate); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dispensa, err := h.HandleCreateDispensa(dispensaTemplate, CreateContext(c))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(dispensa)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import "log"
|
||||||
|
|
||||||
|
func Must[T any](value T, err error) T {
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
+3
-3
@@ -2,11 +2,11 @@ package util
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
"encoding/hex"
|
||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GenerateRandomString generates a random base64 string from a random array of "n" bytes (to prevent padding let n be a multiple of 3)
|
// GenerateRandomString generates a random hex string from a random array of "n" bytes
|
||||||
func GenerateRandomString(n int) string {
|
func GenerateRandomString(n int) string {
|
||||||
b := make([]byte, n)
|
b := make([]byte, n)
|
||||||
|
|
||||||
@@ -15,5 +15,5 @@ func GenerateRandomString(n int) string {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return base64.URLEncoding.EncodeToString(b)
|
return hex.EncodeToString(b)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user