mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-03-25 16:45:05 +00:00
Starting with mail settings
This commit is contained in:
parent
9020d9aa3b
commit
2e3454c7a1
@ -148,7 +148,8 @@ val store = project.in(file("modules/store")).
|
||||
Dependencies.fs2 ++
|
||||
Dependencies.databases ++
|
||||
Dependencies.flyway ++
|
||||
Dependencies.loggingApi
|
||||
Dependencies.loggingApi ++
|
||||
Dependencies.emil
|
||||
).dependsOn(common)
|
||||
|
||||
val text = project.in(file("modules/text")).
|
||||
@ -225,7 +226,8 @@ val backend = project.in(file("modules/backend")).
|
||||
Dependencies.loggingApi ++
|
||||
Dependencies.fs2 ++
|
||||
Dependencies.bcrypt ++
|
||||
Dependencies.http4sClient
|
||||
Dependencies.http4sClient ++
|
||||
Dependencies.emil
|
||||
).dependsOn(store)
|
||||
|
||||
val webapp = project.in(file("modules/webapp")).
|
||||
|
@ -1212,8 +1212,172 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/BasicResult"
|
||||
/sec/email/settings:
|
||||
get:
|
||||
tags: [ E-Mail ]
|
||||
summary: List email settings for current user.
|
||||
description: |
|
||||
List all available e-mail settings for the current user.
|
||||
E-Mail settings specify smtp connections that can be used to
|
||||
sent e-mails.
|
||||
|
||||
Multiple e-mail settings can be specified, they are
|
||||
distinguished by their `name`.
|
||||
responses:
|
||||
200:
|
||||
description: Ok
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EmailSettingsList"
|
||||
post:
|
||||
tags: [ E-Mail ]
|
||||
summary: Create new email settings
|
||||
description: |
|
||||
Create new e-mail settings.
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EmailSettings"
|
||||
responses:
|
||||
200:
|
||||
description: Ok
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/BasicResult"
|
||||
/sec/email/settings/{name}:
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/name"
|
||||
get:
|
||||
tags: [ E-Mail ]
|
||||
summary: Return specific email settings by name.
|
||||
description: |
|
||||
Return the stored e-mail settings for the given connection
|
||||
name.
|
||||
responses:
|
||||
200:
|
||||
description: Ok
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EmailSettings"
|
||||
put:
|
||||
tags: [ E-Mail ]
|
||||
summary: Change specific email settings.
|
||||
description: |
|
||||
Changes all settings for the connection with the given `name`.
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EmailSettings"
|
||||
responses:
|
||||
200:
|
||||
description: Ok
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/BasicResult"
|
||||
delete:
|
||||
tags: [ E-Mail ]
|
||||
summary: Delete e-mail settings.
|
||||
description: |
|
||||
Deletes the e-mail settings with the specified `name`.
|
||||
responses:
|
||||
200:
|
||||
description: Ok
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/BasicResult"
|
||||
|
||||
/sec/email/send/{name}/{id}:
|
||||
post:
|
||||
tags: [ E-Mail ]
|
||||
summary: Send an email.
|
||||
description: |
|
||||
Sends an email as specified with all attachments of the item
|
||||
with `id` as mail attachments. If the item has no attachments,
|
||||
then the mail is sent without any. If the item's attachments
|
||||
exceed a specific size, the mail will not be sent.
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/name"
|
||||
- $ref: "#/components/parameters/id"
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/SimpleMail"
|
||||
responses:
|
||||
200:
|
||||
description: Ok
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/BasicResult"
|
||||
|
||||
components:
|
||||
schemas:
|
||||
SimpleMail:
|
||||
description: |
|
||||
A simple e-mail.
|
||||
required:
|
||||
- recipients
|
||||
- subject
|
||||
- body
|
||||
properties:
|
||||
recipients:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
subject:
|
||||
type: string
|
||||
body:
|
||||
type: string
|
||||
EmailSettingsList:
|
||||
description: |
|
||||
A list of user email settings.
|
||||
required:
|
||||
- items
|
||||
properties:
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/EmailSettings"
|
||||
EmailSettings:
|
||||
description: |
|
||||
SMTP settings for sending mail.
|
||||
required:
|
||||
- name
|
||||
- smtpHost
|
||||
- smtpPort
|
||||
- smtpUser
|
||||
- smtpPassword
|
||||
- from
|
||||
- sslType
|
||||
- certificateCheck
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
format: ident
|
||||
smtpHost:
|
||||
type: string
|
||||
smtpPort:
|
||||
type: integer
|
||||
format: int32
|
||||
smtpUser:
|
||||
type: string
|
||||
smtpPassword:
|
||||
type: string
|
||||
format: password
|
||||
from:
|
||||
type: string
|
||||
sslType:
|
||||
type: string
|
||||
certificateCheck:
|
||||
type: boolean
|
||||
CheckFileResult:
|
||||
description: |
|
||||
Results when searching for file checksums.
|
||||
@ -2157,7 +2321,7 @@ components:
|
||||
id:
|
||||
name: id
|
||||
in: path
|
||||
description: A identifier
|
||||
description: An identifier
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
@ -2182,3 +2346,10 @@ components:
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
name:
|
||||
name: name
|
||||
in: path
|
||||
description: An e-mail connection name
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
|
@ -0,0 +1,16 @@
|
||||
CREATE TABLE "useremail" (
|
||||
"id" varchar(254) not null primary key,
|
||||
"uid" varchar(254) not null,
|
||||
"name" varchar(254) not null,
|
||||
"smtp_host" varchar(254) not null,
|
||||
"smtp_port" int not null,
|
||||
"smtp_user" varchar(254) not null,
|
||||
"smtp_password" varchar(254) not null,
|
||||
"smtp_ssl" varchar(254) not null,
|
||||
"smtp_certcheck" boolean not null,
|
||||
"mail_from" varchar(254) not null,
|
||||
"mail_replyto" varchar(254),
|
||||
"created" timestamp not null,
|
||||
unique ("uid", "name"),
|
||||
foreign key ("uid") references "user_"("uid")
|
||||
);
|
@ -12,6 +12,8 @@ import doobie.implicits.legacy.instant._
|
||||
import doobie.util.log.Success
|
||||
import io.circe.{Decoder, Encoder}
|
||||
import docspell.common.syntax.all._
|
||||
import emil.{MailAddress, SSLType}
|
||||
import emil.javamail.syntax._
|
||||
|
||||
trait DoobieMeta {
|
||||
|
||||
@ -88,9 +90,31 @@ trait DoobieMeta {
|
||||
|
||||
implicit val metaLanguage: Meta[Language] =
|
||||
Meta[String].imap(Language.unsafe)(_.iso3)
|
||||
|
||||
implicit val sslType: Meta[SSLType] =
|
||||
Meta[String].imap(DoobieMeta.readSSLType)(DoobieMeta.sslTypeString)
|
||||
|
||||
implicit val mailAddress: Meta[MailAddress] =
|
||||
Meta[String].imap(str => MailAddress.parse(str).fold(sys.error, identity))(_.asUnicodeString)
|
||||
}
|
||||
|
||||
object DoobieMeta extends DoobieMeta {
|
||||
import org.log4s._
|
||||
private val logger = getLogger
|
||||
|
||||
private def readSSLType(str: String): SSLType =
|
||||
str.toLowerCase match {
|
||||
case "ssl" => SSLType.SSL
|
||||
case "starttls" => SSLType.StartTLS
|
||||
case "none" => SSLType.NoEncryption
|
||||
case _ => sys.error(s"Invalid ssl-type: $str")
|
||||
}
|
||||
|
||||
private def sslTypeString(st: SSLType): String =
|
||||
st match {
|
||||
case SSLType.SSL => "ssl"
|
||||
case SSLType.StartTLS => "starttls"
|
||||
case SSLType.NoEncryption => "none"
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,71 @@
|
||||
package docspell.store.records
|
||||
|
||||
import doobie._
|
||||
import doobie.implicits._
|
||||
import docspell.common._
|
||||
import docspell.store.impl.Column
|
||||
import docspell.store.impl.Implicits._
|
||||
import emil.{MailAddress, SSLType}
|
||||
|
||||
case class RUserEmail(
|
||||
id: Ident,
|
||||
uid: Ident,
|
||||
name: String,
|
||||
smtpHost: String,
|
||||
smtpPort: Int,
|
||||
smtpUser: String,
|
||||
smtpPassword: Password,
|
||||
smtpSsl: SSLType,
|
||||
smtpCertCheck: Boolean,
|
||||
mailFrom: MailAddress,
|
||||
mailReplyTo: Option[MailAddress],
|
||||
created: Timestamp
|
||||
) {}
|
||||
|
||||
object RUserEmail {
|
||||
|
||||
val table = fr"useremail"
|
||||
|
||||
object Columns {
|
||||
val id = Column("id")
|
||||
val uid = Column("uid")
|
||||
val name = Column("name")
|
||||
val smtpHost = Column("smtp_host")
|
||||
val smtpPort = Column("smtp_port")
|
||||
val smtpUser = Column("smtp_user")
|
||||
val smtpPass = Column("smtp_password")
|
||||
val smtpSsl = Column("smtp_ssl")
|
||||
val smtpCertCheck = Column("smtp_certcheck")
|
||||
val mailFrom = Column("mail_from")
|
||||
val mailReplyTo = Column("mail_replyto")
|
||||
val created = Column("created")
|
||||
|
||||
val all = List(
|
||||
id,
|
||||
uid,
|
||||
name,
|
||||
smtpHost,
|
||||
smtpPort,
|
||||
smtpUser,
|
||||
smtpPass,
|
||||
smtpSsl,
|
||||
smtpCertCheck,
|
||||
mailFrom,
|
||||
mailReplyTo,
|
||||
created
|
||||
)
|
||||
}
|
||||
|
||||
import Columns._
|
||||
|
||||
def insert(v: RUserEmail): ConnectionIO[Int] =
|
||||
insertRow(
|
||||
table,
|
||||
all,
|
||||
sql"${v.id},${v.uid},${v.name},${v.smtpHost},${v.smtpPort},${v.smtpUser},${v.smtpPassword},${v.smtpSsl},${v.smtpCertCheck},${v.mailFrom},${v.mailReplyTo},${v.created}"
|
||||
).update.run
|
||||
|
||||
def findByUser(userId: Ident): ConnectionIO[Vector[RUserEmail]] =
|
||||
selectSimple(all, table, uid.is(userId)).query[RUserEmail].to[Vector]
|
||||
|
||||
}
|
68
modules/webapp/src/main/elm/Comp/EmailSettingsForm.elm
Normal file
68
modules/webapp/src/main/elm/Comp/EmailSettingsForm.elm
Normal file
@ -0,0 +1,68 @@
|
||||
module Comp.EmailSettingsForm exposing
|
||||
( Model
|
||||
, Msg
|
||||
, emptyModel
|
||||
, update
|
||||
, view
|
||||
)
|
||||
|
||||
import Api.Model.EmailSettings exposing (EmailSettings)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onInput)
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ settings : EmailSettings
|
||||
, name : String
|
||||
}
|
||||
|
||||
|
||||
emptyModel : Model
|
||||
emptyModel =
|
||||
{ settings = Api.Model.EmailSettings.empty
|
||||
, name = ""
|
||||
}
|
||||
|
||||
|
||||
init : EmailSettings -> Model
|
||||
init ems =
|
||||
{ settings = ems
|
||||
, name = ems.name
|
||||
}
|
||||
|
||||
|
||||
type Msg
|
||||
= SetName String
|
||||
|
||||
|
||||
isValid : Model -> Bool
|
||||
isValid model =
|
||||
True
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update msg model =
|
||||
( model, Cmd.none )
|
||||
|
||||
|
||||
view : Model -> Html Msg
|
||||
view model =
|
||||
div
|
||||
[ classList
|
||||
[ ( "ui form", True )
|
||||
, ( "error", not (isValid model) )
|
||||
, ( "success", isValid model )
|
||||
]
|
||||
]
|
||||
[ div [ class "required field" ]
|
||||
[ label [] [ text "Name" ]
|
||||
, input
|
||||
[ type_ "text"
|
||||
, value model.name
|
||||
, onInput SetName
|
||||
, placeholder "Connection name"
|
||||
]
|
||||
[]
|
||||
]
|
||||
]
|
70
modules/webapp/src/main/elm/Comp/EmailSettingsManage.elm
Normal file
70
modules/webapp/src/main/elm/Comp/EmailSettingsManage.elm
Normal file
@ -0,0 +1,70 @@
|
||||
module Comp.EmailSettingsManage exposing
|
||||
( Model
|
||||
, Msg
|
||||
, emptyModel
|
||||
, init
|
||||
, update
|
||||
, view
|
||||
)
|
||||
|
||||
import Api
|
||||
import Api.Model.BasicResult exposing (BasicResult)
|
||||
import Api.Model.EmailSettings exposing (EmailSettings)
|
||||
import Api.Model.EmailSettingsList exposing (EmailSettingsList)
|
||||
import Comp.EmailSettingsForm
|
||||
import Comp.EmailSettingsTable
|
||||
import Comp.YesNoDimmer
|
||||
import Data.Flags exposing (Flags)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ tableModel : Comp.EmailSettingsTable.Model
|
||||
, formModel : Comp.EmailSettingsForm.Model
|
||||
, viewMode : ViewMode
|
||||
, formError : Maybe String
|
||||
, loading : Bool
|
||||
, deleteConfirm : Comp.YesNoDimmer.Model
|
||||
}
|
||||
|
||||
|
||||
emptyModel : Model
|
||||
emptyModel =
|
||||
{ tableModel = Comp.EmailSettingsTable.emptyModel
|
||||
, formModel = Comp.EmailSettingsForm.emptyModel
|
||||
, viewMode = Table
|
||||
, formError = Nothing
|
||||
, loading = False
|
||||
, deleteConfirm = Comp.YesNoDimmer.emptyModel
|
||||
}
|
||||
|
||||
|
||||
init : ( Model, Cmd Msg )
|
||||
init =
|
||||
( emptyModel, Cmd.none )
|
||||
|
||||
|
||||
type ViewMode
|
||||
= Table
|
||||
| Form
|
||||
|
||||
|
||||
type Msg
|
||||
= TableMsg Comp.EmailSettingsTable.Msg
|
||||
| FormMsg Comp.EmailSettingsForm.Msg
|
||||
|
||||
|
||||
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
|
||||
update flags msg model =
|
||||
( model, Cmd.none )
|
||||
|
||||
|
||||
view : Model -> Html Msg
|
||||
view model =
|
||||
case model.viewMode of
|
||||
Table ->
|
||||
Html.map TableMsg (Comp.EmailSettingsTable.view model.tableModel)
|
||||
|
||||
Form ->
|
||||
Html.map FormMsg (Comp.EmailSettingsForm.view model.formModel)
|
49
modules/webapp/src/main/elm/Comp/EmailSettingsTable.elm
Normal file
49
modules/webapp/src/main/elm/Comp/EmailSettingsTable.elm
Normal file
@ -0,0 +1,49 @@
|
||||
module Comp.EmailSettingsTable exposing
|
||||
( Model
|
||||
, Msg
|
||||
, emptyModel
|
||||
, update
|
||||
, view
|
||||
)
|
||||
|
||||
import Api.Model.EmailSettings exposing (EmailSettings)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ emailSettings : List EmailSettings
|
||||
}
|
||||
|
||||
|
||||
emptyModel : Model
|
||||
emptyModel =
|
||||
init []
|
||||
|
||||
|
||||
init : List EmailSettings -> Model
|
||||
init ems =
|
||||
{ emailSettings = ems
|
||||
}
|
||||
|
||||
|
||||
type Msg
|
||||
= Select EmailSettings
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update msg model =
|
||||
( model, Cmd.none )
|
||||
|
||||
|
||||
view : Model -> Html Msg
|
||||
view model =
|
||||
table [ class "ui table" ]
|
||||
[ thead []
|
||||
[ th [] [ text "Name" ]
|
||||
, th [] [ text "Host/Port" ]
|
||||
, th [] [ text "From" ]
|
||||
]
|
||||
, tbody []
|
||||
[]
|
||||
]
|
@ -6,11 +6,13 @@ module Page.UserSettings.Data exposing
|
||||
)
|
||||
|
||||
import Comp.ChangePasswordForm
|
||||
import Comp.EmailSettingsManage
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ currentTab : Maybe Tab
|
||||
, changePassModel : Comp.ChangePasswordForm.Model
|
||||
, emailSettingsModel : Comp.EmailSettingsManage.Model
|
||||
}
|
||||
|
||||
|
||||
@ -18,13 +20,16 @@ emptyModel : Model
|
||||
emptyModel =
|
||||
{ currentTab = Nothing
|
||||
, changePassModel = Comp.ChangePasswordForm.emptyModel
|
||||
, emailSettingsModel = Comp.EmailSettingsManage.emptyModel
|
||||
}
|
||||
|
||||
|
||||
type Tab
|
||||
= ChangePassTab
|
||||
| EmailSettingsTab
|
||||
|
||||
|
||||
type Msg
|
||||
= SetTab Tab
|
||||
| ChangePassMsg Comp.ChangePasswordForm.Msg
|
||||
| EmailSettingsMsg Comp.EmailSettingsManage.Msg
|
||||
|
@ -1,6 +1,7 @@
|
||||
module Page.UserSettings.Update exposing (update)
|
||||
|
||||
import Comp.ChangePasswordForm
|
||||
import Comp.EmailSettingsManage
|
||||
import Data.Flags exposing (Flags)
|
||||
import Page.UserSettings.Data exposing (..)
|
||||
|
||||
@ -21,3 +22,10 @@ update flags msg model =
|
||||
Comp.ChangePasswordForm.update flags m model.changePassModel
|
||||
in
|
||||
( { model | changePassModel = m2 }, Cmd.map ChangePassMsg c2 )
|
||||
|
||||
EmailSettingsMsg m ->
|
||||
let
|
||||
( m2, c2 ) =
|
||||
Comp.EmailSettingsManage.update flags m model.emailSettingsModel
|
||||
in
|
||||
( { model | emailSettingsModel = m2 }, Cmd.map EmailSettingsMsg c2 )
|
||||
|
@ -1,6 +1,7 @@
|
||||
module Page.UserSettings.View exposing (view)
|
||||
|
||||
import Comp.ChangePasswordForm
|
||||
import Comp.EmailSettingsManage
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onClick)
|
||||
@ -17,13 +18,22 @@ view model =
|
||||
]
|
||||
, div [ class "ui attached fluid segment" ]
|
||||
[ div [ class "ui fluid vertical secondary menu" ]
|
||||
[ div
|
||||
[ a
|
||||
[ classActive (model.currentTab == Just ChangePassTab) "link icon item"
|
||||
, onClick (SetTab ChangePassTab)
|
||||
, href "#"
|
||||
]
|
||||
[ i [ class "user secret icon" ] []
|
||||
, text "Change Password"
|
||||
]
|
||||
, a
|
||||
[ classActive (model.currentTab == Just EmailSettingsTab) "link icon item"
|
||||
, onClick (SetTab EmailSettingsTab)
|
||||
, href "#"
|
||||
]
|
||||
[ i [ class "mail icon" ] []
|
||||
, text "E-Mail Settings"
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
@ -33,6 +43,9 @@ view model =
|
||||
Just ChangePassTab ->
|
||||
viewChangePassword model
|
||||
|
||||
Just EmailSettingsTab ->
|
||||
viewEmailSettings model
|
||||
|
||||
Nothing ->
|
||||
[]
|
||||
)
|
||||
@ -40,6 +53,18 @@ view model =
|
||||
]
|
||||
|
||||
|
||||
viewEmailSettings : Model -> List (Html Msg)
|
||||
viewEmailSettings model =
|
||||
[ h2 [ class "ui header" ]
|
||||
[ i [ class "mail icon" ] []
|
||||
, div [ class "content" ]
|
||||
[ text "E-Mail Settings"
|
||||
]
|
||||
]
|
||||
, Html.map EmailSettingsMsg (Comp.EmailSettingsManage.view model.emailSettingsModel)
|
||||
]
|
||||
|
||||
|
||||
viewChangePassword : Model -> List (Html Msg)
|
||||
viewChangePassword model =
|
||||
[ h2 [ class "ui header" ]
|
||||
|
@ -9,6 +9,7 @@ object Dependencies {
|
||||
val BitpeaceVersion = "0.4.2"
|
||||
val CirceVersion = "0.12.3"
|
||||
val DoobieVersion = "0.8.8"
|
||||
val EmilVersion = "0.1.1"
|
||||
val FastparseVersion = "2.1.3"
|
||||
val FlywayVersion = "6.1.3"
|
||||
val Fs2Version = "2.1.0"
|
||||
@ -26,6 +27,11 @@ object Dependencies {
|
||||
val TikaVersion = "1.23"
|
||||
val YamuscaVersion = "0.6.1"
|
||||
|
||||
val emil = Seq(
|
||||
"com.github.eikek" %% "emil-common" % EmilVersion,
|
||||
"com.github.eikek" %% "emil-javamail" % EmilVersion
|
||||
)
|
||||
|
||||
val stanfordNlpCore = Seq(
|
||||
"edu.stanford.nlp" % "stanford-corenlp" % StanfordNlpVersion excludeAll(
|
||||
ExclusionRule("com.io7m.xom", "xom"),
|
||||
|
Loading…
x
Reference in New Issue
Block a user