Peace/lib/Peace/DB.pm

207 lines
5.5 KiB
Perl

package Peace::DB;
use v5.30.0;
use strict;
use warnings;
use Params::ValidationCompiler qw/validation_for/;
use Types::Standard qw/HashRef/;
use DBI;
use Const::Fast;
my @migrations = (
'CREATE TABLE options (
key TEXT PRIMARY KEY,
value INTEGER
);',
'CREATE EXTENSION "pgcrypto";',
'CREATE TABLE customers (
uuid UUID NOT NULL DEFAULT gen_random_uuid(),
date_creation timestamp DEFAULT NOW(),
secret_bcrypt TEXT NOT NULL,
stripe_id TEXT,
PRIMARY KEY (uuid)
);',
'CREATE TABLE developers (
uuid UUID NOT NULL DEFAULT gen_random_uuid(),
date_creation timestamp DEFAULT NOW(),
secret_bcrypt TEXT NOT NULL,
name TEXT NOT NULL,
surname TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
stripe_id TEXT,
country TEXT NOT NULL,
verified BOOL DEFAULT false,
PRIMARY KEY (uuid)
);',
'CREATE TABLE applications (
uuid UUID NOT NULL DEFAULT gen_random_uuid(),
date_creation timestamp DEFAULT NOW(),
name TEXT NOT NULL,
description TEXT NOT NULL,
url TEXT NOT NULL,
developer UUID NOT NULL,
price INTEGER NOT NULL,
git_repo TEXT NOT NULL,
app_id TEXT NOT NULL UNIQUE,
flatpak_builder_file TEXT NOT NULL,
flatpak_repo TEXT NOT NULL,
verified BOOL DEFAULT false,
PRIMARY KEY (uuid),
FOREIGN KEY (developer) REFERENCES developers (uuid)
);',
'CREATE TABLE releases (
uuid UUID NOT NULL DEFAULT gen_random_uuid(),
date_creation timestamp DEFAULT NOW(),
application UUID NOT NULL,
tag TEXT NOT NULL,
name TEXT NOT NULL,
PRIMARY KEY (uuid),
FOREIGN KEY (application) REFERENCES applications (uuid)
);',
'CREATE TABLE builds (
uuid UUID NOT NULL DEFAULT gen_random_uuid(),
date_creation timestamp DEFAULT NOW(),
release UUID NOT NULL,
success BOOLEAN NOT NULL,
log TEXT NOT NULL,
arch TEXT NOT NULL,
PRIMARY KEY (uuid),
FOREIGN KEY (release) REFERENCES releases (uuid)
);',
'CREATE TABLE purchases (
customer UUID NOT NULL,
application UUID NOT NULL,
date_purchase timestamp DEFAULT NOW(),
PRIMARY KEY (customer, application),
FOREIGN KEY (application) REFERENCES applications (uuid),
FOREIGN KEY (customer) REFERENCES customers (uuid)
);',
);
{
my $validator = validation_for(
params => {
config => { type => HashRef }
}
);
sub dbh {
my $self = shift;
my %params = $validator->(@_);
my $config = $params{config};
my $db_config = $config->{db_config};
my $dbname = $db_config->{dbname} or die 'No dbname in db_config';
my $host = $db_config->{host} or die 'No host in db_config';
my $username = $db_config->{username} or die 'No username in db_config';
my $password = $db_config->{password} or die 'No password in db_config';
my $port = $db_config->{port};
my $dsn =
"dbi:Pg:"
. ( defined $dbname ? "dbname=$dbname;" : "" )
. ( defined $host ? "host=$host;" : "" )
. ( defined $port ? "port=$port;" : "" );
my $dbh = DBI->connect_cached(
$dsn,
$username,
$password,
{
AutoCommit => 1,
RaiseError => 1,
sqlite_unicode => 1,
}
);
state $migrations_run = 0;
if ( !$migrations_run ) {
run_migrations($dbh);
$migrations_run = 1;
}
return $dbh;
}
}
sub run_migrations {
my $dbh = shift;
my $current_migration = _get_current_migration_number($dbh);
local $dbh->{RaiseError} = 0;
if ( $current_migration < scalar @migrations ) {
my @needed_migrations =
@migrations[ $current_migration .. $#migrations ];
for my $migration (@needed_migrations) {
$dbh->do($migration);
my $update_result =
$dbh->do( <<'EOF', undef, ++$current_migration );
UPDATE options SET value = ? WHERE key = 'migration';
EOF
unless ( defined $update_result && 0+$update_result ) {
say $dbh->do( <<'EOF', undef, $current_migration);
INSERT INTO options (key, value) VALUES ('migration', ?);
EOF
}
}
}
}
sub _get_current_migration_number {
my $dbh = shift;
local $dbh->{RaiseError} = 0;
my $migration = $dbh->selectrow_hashref( <<'EOF', {} );
SELECT value FROM options WHERE key = 'migration'
EOF
my $value = 0;
if ( defined $migration ) {
$value = $migration->{value};
}
return $value;
}
1;
=encoding utf8
=head1 NAME
Peace::DB - Database handler generator for the Peace shop.
=head1 SYNOPSIS
my $peace = Peace->new;
my $home = $ENV{HOME};
my $config =
$peace->plugin(
JSONConfig => {
file => "$home/.config/peace/peace.conf"
}
);
my $dbh = Peace::DB->dbh( config => $config );
=head1 DESCRIPTION
This module helps to recover a database handle and runs
the migrations automatically when doing so.
=head1 FUNCTIONS
Peace::DB implements the following functions:
=head2 dbh
Recovers a database handle, requires the config of the app as
its parameter.
my $dbh = Peace::DB->dbh( config => $config );
=head2 run_migrations
Runs the migrations manually.
Peace::DB->run_migrations($dbh);
=head1 SEE ALSO
L<DBI>, L<DBD::Pg>, L<Peace>